From dad4def23e6faf62b8ea5d003ed993d4a86c7eac Mon Sep 17 00:00:00 2001 From: alanb Date: Fri, 15 Oct 2010 15:09:37 +0100 Subject: [PATCH] 6743526: (bf) -XX:MaxDirectMemorySize= limits memory usage rather than total capacity as intended Reviewed-by: chegar --- src/share/classes/java/nio/Bits.java | 18 ++-- test/java/nio/Buffer/LimitDirectMemory.java | 112 ++++++++++++++++++++ test/java/nio/Buffer/LimitDirectMemory.sh | 99 +++++++++++++++++ 3 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 test/java/nio/Buffer/LimitDirectMemory.java create mode 100644 test/java/nio/Buffer/LimitDirectMemory.sh diff --git a/src/share/classes/java/nio/Bits.java b/src/share/classes/java/nio/Bits.java index fa11b6be7..54f004b6e 100644 --- a/src/share/classes/java/nio/Bits.java +++ b/src/share/classes/java/nio/Bits.java @@ -622,7 +622,7 @@ class Bits { // package-private // initialization if it is launched with "-XX:MaxDirectMemorySize=". private static volatile long maxMemory = VM.maxDirectMemory(); private static volatile long reservedMemory; - private static volatile long usedMemory; + private static volatile long totalCapacity; private static volatile long count; private static boolean memoryLimitSet = false; @@ -630,15 +630,17 @@ class Bits { // package-private // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. static void reserveMemory(long size, int cap) { - synchronized (Bits.class) { if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } - if (size <= maxMemory - reservedMemory) { + // -XX:MaxDirectMemorySize limits the total capacity rather than the + // actual memory usage, which will differ when buffers are page + // aligned. + if (cap <= maxMemory - totalCapacity) { reservedMemory += size; - usedMemory += cap; + totalCapacity += cap; count++; return; } @@ -652,10 +654,10 @@ class Bits { // package-private Thread.currentThread().interrupt(); } synchronized (Bits.class) { - if (reservedMemory + size > maxMemory) + if (totalCapacity + cap > maxMemory) throw new OutOfMemoryError("Direct buffer memory"); reservedMemory += size; - usedMemory += cap; + totalCapacity += cap; count++; } @@ -664,7 +666,7 @@ class Bits { // package-private static synchronized void unreserveMemory(long size, int cap) { if (reservedMemory > 0) { reservedMemory -= size; - usedMemory -= cap; + totalCapacity -= cap; count--; assert (reservedMemory > -1); } @@ -689,7 +691,7 @@ class Bits { // package-private } @Override public long getTotalCapacity() { - return Bits.usedMemory; + return Bits.totalCapacity; } @Override public long getMemoryUsed() { diff --git a/test/java/nio/Buffer/LimitDirectMemory.java b/test/java/nio/Buffer/LimitDirectMemory.java new file mode 100644 index 000000000..597e4d9c0 --- /dev/null +++ b/test/java/nio/Buffer/LimitDirectMemory.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.ByteBuffer; +import java.util.Properties; + +public class LimitDirectMemory { + private static int K = 1024; + + public static void main(String [] args) throws Exception { + if (args.length < 2) + throw new RuntimeException(); + boolean throwp = parseThrow(args[0]); + int size = parseSize(args[1]); + int incr = (args.length > 2 ? parseSize(args[2]) : size); + + Properties p = System.getProperties(); + if (p.getProperty("sun.nio.MaxDirectMemorySize") != null) + throw new RuntimeException("sun.nio.MaxDirectMemorySize defined"); + + ByteBuffer [] b = new ByteBuffer[K]; + + // Fill up most/all of the direct memory + int i = 0; + while (size >= incr) { + b[i++] = ByteBuffer.allocateDirect(incr); + size -= incr; + } + + if (throwp) { + try { + b[i] = ByteBuffer.allocateDirect(incr); + throw new RuntimeException("OutOfMemoryError not thrown: " + + incr); + } catch (OutOfMemoryError e) { + e.printStackTrace(System.out); + System.out.println("OK - Error thrown as expected "); + } + } else { + b[i] = ByteBuffer.allocateDirect(incr); + System.out.println("OK - Error not thrown"); + } + } + + private static boolean parseThrow(String s) { + if (s.equals("true")) return true; + if (s.equals("false")) return false; + throw new RuntimeException("Unrecognized expectation: " + s); + } + + private static int parseSize(String size) throws Exception { + + if (size.equals("DEFAULT")) + return (int)Runtime.getRuntime().maxMemory(); + if (size.equals("DEFAULT+1")) + return (int)Runtime.getRuntime().maxMemory() + 1; + if (size.equals("DEFAULT+1M")) + return (int)Runtime.getRuntime().maxMemory() + (1 << 20); + if (size.equals("DEFAULT-1")) + return (int)Runtime.getRuntime().maxMemory() - 1; + if (size.equals("DEFAULT/2")) + return (int)Runtime.getRuntime().maxMemory() / 2; + + int idx = 0, len = size.length(); + + int result = 1; + for (int i = 0; i < len; i++) { + if (Character.isDigit(size.charAt(i))) idx++; + else break; + } + + if (idx == 0) + throw new RuntimeException("No digits detected: " + size); + + result = Integer.parseInt(size.substring(0, idx)); + + if (idx < len) { + for (int i = idx; i < len; i++) { + switch(size.charAt(i)) { + case 'T': case 't': result *= K; // fall through + case 'G': case 'g': result *= K; // fall through + case 'M': case 'm': result *= K; // fall through + case 'K': case 'k': result *= K; + break; + default: + throw new RuntimeException("Unrecognized size: " + size); + } + } + } + return result; + } +} diff --git a/test/java/nio/Buffer/LimitDirectMemory.sh b/test/java/nio/Buffer/LimitDirectMemory.sh new file mode 100644 index 000000000..3ac1056a7 --- /dev/null +++ b/test/java/nio/Buffer/LimitDirectMemory.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +# +# Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# @test +# @bug 4627316 6743526 +# @summary Test option to limit direct memory allocation +# +# @build LimitDirectMemory +# @run shell LimitDirectMemory.sh + +# set platform-dependent variable +OS=`uname -s` +case "$OS" in + SunOS | Linux ) TMP=/tmp ;; + Windows* ) TMP="c:/temp" ;; + * ) + echo "Unrecognized system!" + exit 1; + ;; +esac + +TMP1=${TMP}/tmp1_$$ + +runTest() { + echo "Testing: $*" + ${TESTJAVA}/bin/java $* + if [ $? -eq 0 ] + then echo "--- passed as expected" + else + echo "--- failed" + exit 1 + fi +} + +launchFail() { + echo "Testing: -XX:MaxDirectMemorySize=$* -cp ${TESTCLASSES} \ + LimitDirectMemory true DEFAULT DEFAULT+1M" + ${TESTJAVA}/bin/java -XX:MaxDirectMemorySize=$* -cp ${TESTCLASSES} \ + LimitDirectMemory true DEFAULT DEFAULT+1M > ${TMP1} 2>&1 + cat ${TMP1} + cat ${TMP1} | grep -s "Unrecognized VM option: \'MaxDirectMemorySize=" + if [ $? -ne 0 ] + then echo "--- failed as expected" + else + echo "--- failed" + exit 1 + fi +} + +# $java LimitDirectMemory throwp fill_direct_memory size_per_buffer + +# Memory is properly limited using multiple buffers. +runTest -XX:MaxDirectMemorySize=10 -cp ${TESTCLASSES} LimitDirectMemory true 10 1 +runTest -XX:MaxDirectMemorySize=1k -cp ${TESTCLASSES} LimitDirectMemory true 1k 100 +runTest -XX:MaxDirectMemorySize=10m -cp ${TESTCLASSES} LimitDirectMemory true 10m 10m + +# We can increase the amount of available memory. +runTest -XX:MaxDirectMemorySize=65M -cp ${TESTCLASSES} \ + LimitDirectMemory false 64M 65M + +# Exactly the default amount of memory is available. +runTest -cp ${TESTCLASSES} LimitDirectMemory false 10 1 +runTest -cp ${TESTCLASSES} LimitDirectMemory false 0 DEFAULT +runTest -cp ${TESTCLASSES} LimitDirectMemory true 0 DEFAULT+1 + +# We should be able to eliminate direct memory allocation entirely. +runTest -XX:MaxDirectMemorySize=0 -cp ${TESTCLASSES} LimitDirectMemory true 0 1 + +# Setting the system property should not work so we should be able to allocate +# the default amount. +runTest -Dsun.nio.MaxDirectMemorySize=1K -cp ${TESTCLASSES} \ + LimitDirectMemory false DEFAULT-1 DEFAULT/2 + +# Various bad values fail to launch the VM. +launchFail foo +launchFail 10kmt +launchFail -1 -- GitLab