提交 739e98e2 编写于 作者: M mchung

6977738: Deadlock between java.lang.ClassLoader and java.util.Properties

Reviewed-by: alanb, sherman, darcy, igor
上级 5bd47730
......@@ -465,14 +465,11 @@ JAVA_JAVA_java = \
java/security/ProtectionDomain.java \
java/net/URLClassLoader.java \
java/net/URLConnection.java \
sun/misc/BootClassLoaderHook.java \
sun/misc/Launcher.java \
sun/misc/MetaIndex.java \
sun/misc/URLClassPath.java \
sun/misc/Version.java \
sun/net/www/protocol/jar/Handler.java \
sun/net/www/protocol/jar/JarURLConnection.java \
sun/net/www/protocol/file/Handler.java \
sun/net/www/protocol/file/FileURLConnection.java \
sun/misc/FileURLMapper.java \
sun/misc/MessageUtils.java \
sun/misc/GC.java \
......@@ -482,6 +479,10 @@ JAVA_JAVA_java = \
sun/misc/JavaIOFileDescriptorAccess.java \
sun/misc/JavaNioAccess.java \
sun/misc/Perf.java \
sun/misc/PerfCounter.java
sun/misc/PerfCounter.java \
sun/net/www/protocol/jar/Handler.java \
sun/net/www/protocol/jar/JarURLConnection.java \
sun/net/www/protocol/file/Handler.java \
sun/net/www/protocol/file/FileURLConnection.java
FILES_java = $(JAVA_JAVA_java)
......@@ -586,25 +586,13 @@ public final class Integer extends Number implements Comparable<Integer> {
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. During VM initialization the
* getAndRemoveCacheProperties method may be used to get and remove any system
* properites that configure the cache size. At this time, the size of the
* cache may be controlled by the -XX:AutoBoxCacheMax=<size> option.
* The cache is initialized on first usage. The size of the cache
* may be controlled by the -XX:AutoBoxCacheMax=<size> option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
// value of java.lang.Integer.IntegerCache.high property (obtained during VM init)
private static String integerCacheHighPropValue;
static void getAndRemoveCacheProperties() {
if (!sun.misc.VM.isBooted()) {
Properties props = System.getProperties();
integerCacheHighPropValue =
(String)props.remove("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null)
System.setProperties(props); // remove from system props
}
}
private static class IntegerCache {
static final int low = -128;
static final int high;
......@@ -613,6 +601,8 @@ public final class Integer extends Number implements Comparable<Integer> {
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
......
......@@ -53,7 +53,13 @@ import sun.reflect.annotation.AnnotationType;
*/
public final class System {
/* First thing---register the natives */
/* register the natives via the static initializer.
*
* VM will invoke the initializeSystemClass method to complete
* the initialization for this class separated from clinit.
* Note that to use properties set by the VM, see the constraints
* described in the initializeSystemClass method.
*/
private static native void registerNatives();
static {
registerNatives();
......@@ -1096,17 +1102,21 @@ public final class System {
* Initialize the system class. Called after thread initialization.
*/
private static void initializeSystemClass() {
props = new Properties();
initProperties(props);
// There are certain system configurations that may be controlled by
// VM options such as the maximum amount of direct memory and
// Integer cache size used to support the object identity semantics
// of autoboxing. Typically, the library will obtain these values
// from the properties set by the VM. If the properties are for
// internal implementation use only, these properties should be
// removed from the system properties.
//
// See java.lang.Integer.IntegerCache and the
// sun.misc.VM.saveAndRemoveProperties method for example.
props = initSystemProperties();
lineSeparator = props.getProperty("line.separator");
sun.misc.Version.init();
// Gets and removes system properties that configure the Integer
// cache used to support the object identity semantics of autoboxing.
// At this time, the size of the cache may be controlled by the
// vm option -XX:AutoBoxCacheMax=<size>.
Integer.getAndRemoveCacheProperties();
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
......@@ -1127,17 +1137,6 @@ public final class System {
// classes are used.
sun.misc.VM.initializeOSEnvironment();
// Set the maximum amount of direct memory. This value is controlled
// by the vm option -XX:MaxDirectMemorySize=<size>. This method acts
// as an initializer only if it is called before sun.misc.VM.booted().
sun.misc.VM.maxDirectMemory();
// Set a boolean to determine whether ClassLoader.loadClass accepts
// array syntax. This value is controlled by the system property
// "sun.lang.ClassLoader.allowArraySyntax". This method acts as
// an initializer only if it is called before sun.misc.VM.booted().
sun.misc.VM.allowArraySyntax();
// Subsystems that are invoked during initialization can invoke
// sun.misc.VM.isBooted() in order to avoid doing things that should
// wait until the application class loader has been set up.
......@@ -1152,6 +1151,18 @@ public final class System {
setJavaLangAccess();
}
private static Properties initSystemProperties() {
Properties props = new Properties();
initProperties(props); // initialized by the VM
// Save a private copy of the system properties object that
// can only be accessed by the internal implementation. Remove
// certain system properties that are not intended for public access.
sun.misc.VM.saveAndRemoveProperties(props);
return props;
}
private static void setJavaLangAccess() {
// Allow privileged classes outside of java.lang
sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){
......
......@@ -705,7 +705,7 @@ class Properties extends Hashtable<Object,Object> {
* <code>Strings</code>.
*/
@Deprecated
public synchronized void save(OutputStream out, String comments) {
public void save(OutputStream out, String comments) {
try {
store(out, comments);
} catch (IOException e) {
......@@ -890,7 +890,7 @@ class Properties extends Hashtable<Object,Object> {
* @see #loadFromXML(InputStream)
* @since 1.5
*/
public synchronized void storeToXML(OutputStream os, String comment)
public void storeToXML(OutputStream os, String comment)
throws IOException
{
if (os == null)
......@@ -929,8 +929,7 @@ class Properties extends Hashtable<Object,Object> {
* @see #loadFromXML(InputStream)
* @since 1.5
*/
public synchronized void storeToXML(OutputStream os, String comment,
String encoding)
public void storeToXML(OutputStream os, String comment, String encoding)
throws IOException
{
if (os == null)
......
......@@ -141,14 +141,13 @@ class XMLUtils {
comments.appendChild(doc.createTextNode(comment));
}
Set keys = props.keySet();
Iterator i = keys.iterator();
while(i.hasNext()) {
String key = (String)i.next();
Element entry = (Element)properties.appendChild(
doc.createElement("entry"));
entry.setAttribute("key", key);
entry.appendChild(doc.createTextNode(props.getProperty(key)));
synchronized (props) {
for (String key : props.stringPropertyNames()) {
Element entry = (Element)properties.appendChild(
doc.createElement("entry"));
entry.setAttribute("key", key);
entry.appendChild(doc.createTextNode(props.getProperty(key)));
}
}
emitDocument(doc, os, encoding);
}
......
......@@ -85,8 +85,7 @@ class ZipFile implements ZipConstants, Closeable {
static {
// A system prpperty to disable mmap use to avoid vm crash when
// in-use zip file is accidently overwritten by others.
String prop = AccessController.doPrivileged(
new GetPropertyAction("sun.zip.disableMemoryMapping"));
String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping");
usemmap = (prop == null ||
!(prop.length() == 0 || prop.equalsIgnoreCase("true")));
}
......
......@@ -25,13 +25,18 @@
package sun.jkernel;
import java.io.*;
import java.net.URLStreamHandlerFactory;
import java.net.URL;
import java.net.MalformedURLException;
import java.security.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.jar.*;
import java.util.zip.*;
import sun.misc.Launcher;
import sun.misc.BootClassLoaderHook;
import sun.misc.Launcher;
import sun.misc.URLClassPath;
import sun.net.www.ParseUtil;
/**
* Handles the downloading of additional JRE components. The bootstrap class
......@@ -658,31 +663,61 @@ public class DownloadManager extends BootClassLoaderHook {
return getAppDataLocalLow() + getKernelJREDir();
}
/**
* Returns an array of JAR files which have been added to the boot strap
* class path since the JVM was first booted.
*/
public static synchronized File[] getAdditionalBootStrapPaths() {
return additionalBootStrapPaths != null ? additionalBootStrapPaths :
new File[0];
}
// To be revisited:
// How DownloadManager maintains its bootstrap class path.
// sun.misc.Launcher.getBootstrapClassPath() returns
// DownloadManager.getBootstrapClassPath() instead.
//
// So should no longer need to lock the Launcher.class.
// In addition, additionalBootStrapPaths is not really needed
// if it obtains the initial bootclasspath during DownloadManager's
// initialization.
private static void addEntryToBootClassPath(File path) {
// Must acquire these locks in this order
synchronized(Launcher.class) {
synchronized(DownloadManager.class) {
synchronized(DownloadManager.class) {
File[] newBootStrapPaths = new File[
additionalBootStrapPaths.length + 1];
System.arraycopy(additionalBootStrapPaths, 0, newBootStrapPaths,
0, additionalBootStrapPaths.length);
newBootStrapPaths[newBootStrapPaths.length - 1] = path;
additionalBootStrapPaths = newBootStrapPaths;
Launcher.flushBootstrapClassPath();
if (bootstrapClassPath != null)
bootstrapClassPath.addURL(getFileURL(path));
}
}
}
/**
* Returns the kernel's bootstrap class path which includes the additional
* JARs downloaded
*/
private static URLClassPath bootstrapClassPath = null;
private synchronized static
URLClassPath getBootClassPath(URLClassPath bcp,
URLStreamHandlerFactory factory)
{
if (bootstrapClassPath == null) {
bootstrapClassPath = new URLClassPath(bcp.getURLs(), factory);
for (File path : additionalBootStrapPaths) {
bootstrapClassPath.addURL(getFileURL(path));
}
}
return bootstrapClassPath;
}
private static URL getFileURL(File file) {
try {
file = file.getCanonicalFile();
} catch (IOException e) {}
try {
return ParseUtil.fileToEncodedURL(file);
} catch (MalformedURLException e) {
// Should never happen since we specify the protocol...
throw new InternalError();
}
}
/**
* Scan through java.ext.dirs to see if the lib/ext directory is included.
......@@ -1680,8 +1715,10 @@ public class DownloadManager extends BootClassLoaderHook {
}
}
public File[] getAdditionalBootstrapPaths() {
return DownloadManager.getAdditionalBootStrapPaths();
public URLClassPath getBootstrapClassPath(URLClassPath bcp,
URLStreamHandlerFactory factory)
{
return DownloadManager.getBootClassPath(bcp, factory);
}
public boolean isCurrentThreadPrefetching() {
......
......@@ -27,6 +27,8 @@ package sun.misc;
import java.io.File;
import java.io.IOException;
import java.net.URLStreamHandlerFactory;
import sun.misc.URLClassPath;
/**
* BootClassLoaderHook defines an interface for a hook to inject
......@@ -94,20 +96,6 @@ public abstract class BootClassLoaderHook {
}
}
private static final File[] EMPTY_FILE_ARRAY = new File[0];
/**
* Returns bootstrap class paths added by the hook.
*/
public static File[] getBootstrapPaths() {
BootClassLoaderHook hook = getHook();
if (hook != null) {
return hook.getAdditionalBootstrapPaths();
} else {
return EMPTY_FILE_ARRAY;
}
}
/**
* Returns a pathname of a JAR or class that the hook loads
* per this loadClass request; or null.
......@@ -133,10 +121,13 @@ public abstract class BootClassLoaderHook {
public abstract boolean loadLibrary(String libname);
/**
* Returns additional boot class paths added by the hook that
* should be searched by the boot class loader.
* Returns a bootstrap class path constructed by the hook.
*
* @param bcp VM's bootstrap class path
* @param factory Launcher's URL stream handler
*/
public abstract File[] getAdditionalBootstrapPaths();
public abstract URLClassPath getBootstrapClassPath(URLClassPath bcp,
URLStreamHandlerFactory factory);
/**
* Returns true if the current thread is in the process of doing
......
......@@ -47,7 +47,6 @@ import java.security.Permissions;
import java.security.Permission;
import java.security.ProtectionDomain;
import java.security.CodeSource;
import sun.security.action.GetPropertyAction;
import sun.security.util.SecurityConstants;
import sun.net.www.ParseUtil;
......@@ -57,6 +56,8 @@ Launcher */
public class Launcher {
private static URLStreamHandlerFactory factory = new Factory();
private static Launcher launcher = new Launcher();
private static String bootClassPath =
System.getProperty("sun.boot.class.path");
public static Launcher getLauncher() {
return launcher;
......@@ -227,7 +228,8 @@ public class Launcher {
File dir = new File(urls[i].getPath()).getParentFile();
if (dir != null && !dir.equals(prevDir)) {
// Look in architecture-specific subdirectory first
String arch = System.getProperty("os.arch");
// Read from the saved system properties to avoid deadlock
String arch = VM.getSavedProperty("os.arch");
if (arch != null) {
File file = new File(new File(dir, arch), name);
if (file.exists()) {
......@@ -377,19 +379,15 @@ public class Launcher {
}
}
private static URLClassPath bootstrapClassPath;
public static synchronized URLClassPath getBootstrapClassPath() {
if (bootstrapClassPath == null) {
String prop = AccessController.doPrivileged(
new GetPropertyAction("sun.boot.class.path"));
private static class BootClassPathHolder {
static final URLClassPath bcp;
static {
URL[] urls;
if (prop != null) {
final String path = prop;
if (bootClassPath != null) {
urls = AccessController.doPrivileged(
new PrivilegedAction<URL[]>() {
public URL[] run() {
File[] classPath = getClassPath(path);
File[] classPath = getClassPath(bootClassPath);
int len = classPath.length;
Set<File> seenDirs = new HashSet<File>();
for (int i = 0; i < len; i++) {
......@@ -410,25 +408,16 @@ public class Launcher {
} else {
urls = new URL[0];
}
bootstrapClassPath = new URLClassPath(urls, factory);
final File[] additionalBootStrapPaths =
BootClassLoaderHook.getBootstrapPaths();
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
for (int i=0; i<additionalBootStrapPaths.length; i++) {
bootstrapClassPath.addURL(
getFileURL(additionalBootStrapPaths[i]));
}
return null;
}
});
bcp = new URLClassPath(urls, factory);
}
return bootstrapClassPath;
}
public static synchronized void flushBootstrapClassPath() {
bootstrapClassPath = null;
public static URLClassPath getBootstrapClassPath() {
URLClassPath bcp = BootClassPathHolder.bcp;
// if DownloadManager is installed, return the bootstrap class path
// maintained by the Java kernel
BootClassLoaderHook hook = BootClassLoaderHook.getHook();
return hook == null ? bcp : hook.getBootstrapClassPath(bcp, factory);
}
private static URL[] pathToURLs(File[] path) {
......
......@@ -170,33 +170,11 @@ public class VM {
//
private static long directMemory = 64 * 1024 * 1024;
// If this method is invoked during VM initialization, it initializes the
// maximum amount of allocatable direct buffer memory (in bytes) from the
// system property sun.nio.MaxDirectMemorySize. The system property will
// be removed when it is accessed.
//
// If this method is invoked after the VM is booted, it returns the
// maximum amount of allocatable direct buffer memory.
// Returns the maximum amount of allocatable direct buffer memory.
// The directMemory variable is initialized during system initialization
// in the saveAndRemoveProperties method.
//
public static long maxDirectMemory() {
if (booted)
return directMemory;
Properties p = System.getProperties();
String s = (String)p.remove("sun.nio.MaxDirectMemorySize");
System.setProperties(p);
if (s != null) {
if (s.equals("-1")) {
// -XX:MaxDirectMemorySize not given, take default
directMemory = Runtime.getRuntime().maxMemory();
} else {
long l = Long.parseLong(s);
if (l > -1)
directMemory = l;
}
}
return directMemory;
}
......@@ -212,28 +190,84 @@ public class VM {
private static boolean defaultAllowArraySyntax = false;
private static boolean allowArraySyntax = defaultAllowArraySyntax;
// If this method is invoked during VM initialization, it initializes the
// allowArraySyntax boolean based on the value of the system property
// The allowArraySyntax boolean is initialized during system initialization
// in the saveAndRemoveProperties method.
//
// It is initialized based on the value of the system property
// "sun.lang.ClassLoader.allowArraySyntax". If the system property is not
// provided, the default for 1.5 is "true". In 1.6, the default will be
// "false". If the system property is provided, then the value of
// allowArraySyntax will be equal to "true" if Boolean.parseBoolean()
// returns "true". Otherwise, the field will be set to "false".
//
// If this method is invoked after the VM is booted, it returns the
// allowArraySyntax boolean set during initialization.
//
public static boolean allowArraySyntax() {
if (!booted) {
String s
= System.getProperty("sun.lang.ClassLoader.allowArraySyntax");
allowArraySyntax = (s == null
? defaultAllowArraySyntax
: Boolean.parseBoolean(s));
}
return allowArraySyntax;
}
/**
* Returns the system property of the specified key saved at
* system initialization time. This method should only be used
* for the system properties that are not changed during runtime.
* It accesses a private copy of the system properties so
* that user's locking of the system properties object will not
* cause the library to deadlock.
*
* Note that the saved system properties do not include
* the ones set by sun.misc.Version.init().
*
*/
public static String getSavedProperty(String key) {
if (savedProps.isEmpty())
throw new IllegalStateException("Should be non-empty if initialized");
return savedProps.getProperty(key);
}
private static final Properties savedProps = new Properties();
// Save a private copy of the system properties and remove
// the system properties that are not intended for public access.
//
// This method can only be invoked during system initialization.
public static void saveAndRemoveProperties(Properties props) {
if (booted)
throw new IllegalStateException("System initialization has completed");
savedProps.putAll(props);
// Set the maximum amount of direct memory. This value is controlled
// by the vm option -XX:MaxDirectMemorySize=<size>.
// The maximum amount of allocatable direct buffer memory (in bytes)
// from the system property sun.nio.MaxDirectMemorySize set by the VM.
// The system property will be removed.
String s = (String)props.remove("sun.nio.MaxDirectMemorySize");
if (s != null) {
if (s.equals("-1")) {
// -XX:MaxDirectMemorySize not given, take default
directMemory = Runtime.getRuntime().maxMemory();
} else {
long l = Long.parseLong(s);
if (l > -1)
directMemory = l;
}
}
// Set a boolean to determine whether ClassLoader.loadClass accepts
// array syntax. This value is controlled by the system property
// "sun.lang.ClassLoader.allowArraySyntax".
s = props.getProperty("sun.lang.ClassLoader.allowArraySyntax");
allowArraySyntax = (s == null
? defaultAllowArraySyntax
: Boolean.parseBoolean(s));
// Remove other private system properties
// used by java.lang.Integer.IntegerCache
props.remove("java.lang.Integer.IntegerCache.high");
// used by java.util.zip.ZipFile
props.remove("sun.zip.disableMemoryMapping");
}
// Initialize any miscellenous operating system settings that need to be
// set for the class libraries.
//
......
/*
* Copyright (c) 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.util.Properties;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;
import java.io.IOException;
import java.net.URL;
/* @test
* @bug 6977738
* @summary Test ClassLoader.getResource() that should not deadlock
# if another thread is holding the system properties object
*
* @build GetResource
* @run main GetResource
*/
public class GetResource {
CyclicBarrier go = new CyclicBarrier(2);
CyclicBarrier done = new CyclicBarrier(2);
Thread t1, t2;
public GetResource() {
t1 = new Thread() {
public void run() {
Properties prop = System.getProperties();
synchronized (prop) {
System.out.println("Thread 1 ready");
try {
go.await();
prop.put("property", "value");
prop.store(System.out, "");
done.await(); // keep holding the lock until t2 finishes
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
System.out.println("Thread 1 exits");
}
};
t2 = new Thread() {
public void run() {
System.out.println("Thread 2 ready");
try {
go.await(); // wait until t1 holds the lock of the system properties
URL u1 = Thread.currentThread().getContextClassLoader().getResource("unknownresource");
URL u2 = Thread.currentThread().getContextClassLoader().getResource("sun/util/resources/CalendarData.class");
if (u2 == null) {
throw new RuntimeException("Test failed: resource not found");
}
done.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
System.out.println("Thread 2 exits");
}
};
}
public void run() throws Exception {
t1.start();
t2.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
throw e;
}
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
throw e;
}
}
public static void main(String[] args) throws Exception {
new GetResource().run();
}
}
......@@ -24,7 +24,10 @@
import java.io.File;
import java.util.TreeSet;
import java.util.Set;
import java.net.URLStreamHandlerFactory;
import sun.misc.BootClassLoaderHook;
import sun.misc.URLClassPath;
/* @test
* @bug 6888802
......@@ -68,10 +71,6 @@ public class TestHook extends BootClassLoaderHook {
for (String s : copy) {
System.out.println(" Loaded " + s);
}
if (BootClassLoaderHook.getBootstrapPaths().length > 0) {
throw new RuntimeException("Unexpected returned value from getBootstrapPaths()");
}
}
private static void testHook() throws Exception {
......@@ -98,8 +97,9 @@ public class TestHook extends BootClassLoaderHook {
return false;
}
public File[] getAdditionalBootstrapPaths() {
return new File[0];
public URLClassPath getBootstrapClassPath(URLClassPath bcp,
URLStreamHandlerFactory factory) {
return bcp;
}
public boolean isCurrentThreadPrefetching() {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册