-
Bug
-
Resolution: Duplicate
-
P3
-
None
-
10.0.2
-
x86_64
-
windows_7
ADDITIONAL SYSTEM INFORMATION :
Windows 7 64-Bit, JRE 10.0.2 (10.0.1)
A DESCRIPTION OF THE PROBLEM :
A Java main program (running with JRE-A e.g. 10.0.1, located at a/java) is starting a new Java process with an alternate JRE-B (e.g. 10.0.2 located at b/java) and terminates itself. The new process has still a handle to the modules file from JRE-A. Strangely, it has handles to both modules file in a/java/lib and b/java/lib.
REGRESSION : Last worked in version 8u181
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(1) create dir c:\temp\test\
(2) copy JRE 10.0.1 to c:\temp\test\a\java\
directories in c:\temp\test\:
c:\temp\test\a\java\release
c:\temp\test\a\java\README.html
c:\temp\test\a\java\bin\*
...
(3) copy JRE 10.0.2 to c:\temp\test\b\java\
additional directories in c:\temp\test\:
c:\temp\test\b\java\release
c:\temp\test\b\java\README.html
c:\temp\test\b\java\bin\*
...
(4) start cmd terminal window and cd to c:\temp\test
(5) set environment to JRE-A
> set JAVA_HOME=c:\temp\test\a\java
> set JRE_HOME=%JAVA_HOME%
> set PATH=%JAVA_HOME%\bin\;%PATH%
(6) copy TestRestartWithNewJre.class to c:\temp\test
(7) run the test program with parameter -newjre C:\temp\test\b\java
> java -cp . TestRestartWithNewJre -newjre C:\temp\test\b\java
(8) check the result:
(a) check p1.log: it shows the PID, the environment and properties of the starting process (JRE-A)
(b) check the task-manager: the starting process (with JRE-A) does not exist anymore
(c) check p2.log: it shows the PID, the environment and properties of the started process (JRE-B)
There is also an AccessDeniedException. This is thrown, because the renaming of the JRE-A home dir (a\java -> a\java-<timestramp>) failed.
(d) check the task-manager: the started process (with JRE-B) is still in process-list (sleeps for 1h)
(e) The Process-Explorer (procexp.exe, from Windows SysInternals) shows, that the started Java process (JRE-B) has handles to both modules file (from JRE-A and JRE-B)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The started process (using JRE-B) shouldn't have any reference to files in JRE-A
ACTUAL -
The Process-Explorer (procexp.exe, from Windows SysInternals) shows, that
(a) the first process (using JRE-A) does not exist anymore
(b) the started Java process (JRE-B) has handles to both modules file (from JRE-A and JRE-B)
---------- BEGIN SOURCE ----------
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestRestartWithNewJre {
private static String logFile = "p1.log";
private static String oldJreHome = null;
private static String newJreHome = null;
public static void main(String[] args) {
TestRestartWithNewJre t = new TestRestartWithNewJre();
try {
//get params
setParams(args);
//delete logfiles
new File( logFile ).delete();
//log params and all system properties
log("args: " + Arrays.asList(args));
dumpMap("properties: ", (Map) System.getProperties() );
dumpMap("env: ", System.getenv() );
// restart with alternate JRE or rename the old one
t.start();
} catch (IOException e) {
log("IOException found", e);
log("plese check first which file is locked..... (process sleep for 1h)");
try {
Thread.sleep(1000 * 60 * 60);
} catch (InterruptedException e1) {
log("sleep interrupted", e1);
}
} catch (Exception e) {
log("error", e);
}
}
private void start() throws IOException {
// if new jre home is given, restart this job with the new JRE and put the reference to the old JRE as parameter "-oldjre" (for renaming later on)
if( newJreHome != null ) {
File newJreHomeDir = new File(newJreHome);
if( newJreHomeDir.exists() && newJreHomeDir.isDirectory() ) {
List<String> cmdItems = new ArrayList<String>();
String javaExecutable = getJavaExe(newJreHomeDir);
cmdItems.add(javaExecutable);
cmdItems.add("-classpath");
String jarName = getJarName();
cmdItems.add(jarName);
cmdItems.add(this.getClass().getName());
cmdItems.add("-oldjre");
String jreHome = System.getProperty("java.home");
cmdItems.add(jreHome);
cmdItems.add("-logfile");
cmdItems.add("p2.log");
invokeNewProcess(newJreHome, cmdItems);
System.exit(1);
} else {
log("new JRE home '" + newJreHome + "' does not exist");
System.exit(1);
}
} else if( oldJreHome != null ) {
log("sleep for 2 seconds first, before trying to rename old/unused JRE-home");
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException e1) {
log("sleep interrupted", e1);
}
// if old/unused jre home is given, try to rename it
File oldJreHomeDir = new File(oldJreHome);
if( oldJreHomeDir.exists() && oldJreHomeDir.isDirectory() ) {
File targetPath = new File(oldJreHomeDir.getParentFile(), oldJreHomeDir.getName() + "-" + System.currentTimeMillis());
log("try to move '" + oldJreHome + "' to '" + targetPath.getAbsolutePath() + "'");
Path movedTargetPath = Files.move(oldJreHomeDir.toPath(), targetPath.toPath(), StandardCopyOption.ATOMIC_MOVE);
if( movedTargetPath.toFile().exists() ) {
log("rename '" + oldJreHome + "' to '" + movedTargetPath.toString() + "' done with success");
} else {
log("rename '" + oldJreHome + "' to '" + movedTargetPath.toString() + "' failed");
}
} else {
log("wrong parameter -oldjre '" + oldJreHome + "', path does not exist");
System.exit(1);
}
}
}
/*
* get params
*/
private static void setParams(String[] args) {
for( int i=0; i<args.length; i++ ) {
switch( args[i] ) {
case "-oldjre":
if( i+1 < args.length ) {
oldJreHome = args[++i];
}
break;
case "-newjre":
if( i+1 < args.length ) {
newJreHome = args[++i];
}
break;
case "-logfile":
if( i+1 < args.length ) {
logFile = args[++i];
}
break;
}
}
}
/*
* get the name of this jar (for restart with alternate jre)
*/
private String getJarName() {
return this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
}
/*
* determine the path/name of the java executable for the given JRE home (the alternate jre for restart !)
*/
private String getJavaExe(final File javaHomeDir) throws IOException {
final Path start = javaHomeDir.toPath();
final int maxDepth = 5;
final StringBuilder sb = new StringBuilder("");
try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) -> ((String.valueOf(path).endsWith("java.exe")||String.valueOf(path).endsWith("java"))&&path.toFile().isFile()&&(path.toFile().canExecute())))) {
sb.append( stream.sorted().map(String::valueOf).collect(Collectors.joining(";")) );
}
String javaExe[] = sb.toString().split(";");
if( javaExe.length != 1 ) {
throw new RuntimeException("java executable not found: " + Arrays.asList(javaExe));
}
File javaExeFile = new File(javaExe[0]);
boolean javaExecutableFound = javaExeFile != null && javaExeFile.exists() && javaExeFile.isFile() && javaExeFile.canRead() && javaExeFile.canExecute();
if( !javaExecutableFound ) {
throw new RuntimeException("java file not executable: " + javaExeFile.getAbsolutePath());
}
log("getJavaExe: " + javaExeFile.getCanonicalPath() );
return javaExeFile.getCanonicalPath();
}
/*
* invoke the new process (with alternate JRE)
*/
private void invokeNewProcess(String newJavaHome, List<String> cmdItems) throws IOException {
log("try to start process: " + cmdItems);
final ProcessBuilder pb = new ProcessBuilder(cmdItems);
Map<String,String> pbEnv = pb.environment();
if( newJavaHome != null ) {
pbEnv.clear();
pbEnv.put("JRE_HOME", newJavaHome);
pbEnv.put("JAVA_HOME", newJavaHome);
}
dumpMap("env for new process: ", pbEnv);
pb.redirectErrorStream(true);
pb.redirectInput();
final Process process = pb.start();
log("process is started: " + process.isAlive());
}
/*
* log the current map (env or system properties)
*/
private static void dumpMap(final String label, final Map<String,String> map) {
Set<String> keys = map.keySet();
java.util.List<String> sortedKeys = new ArrayList<String>(keys);
Collections.sort(sortedKeys);
StringBuilder buf = new StringBuilder(label).append(System.lineSeparator());
for (Iterator<String> iter = sortedKeys.iterator(); iter.hasNext();) {
String key = iter.next();
String value = map.get(key);
buf.append("\t" + key + "=" + value).append(System.lineSeparator());
}
log(buf.toString());
}
/*
* log msg
*/
private static void log(String msg) {
log(msg, null);
}
/*
* log msg and exception (if not null)
*/
private static void log(String msg, Exception ex ) {
final DateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss SSS ");
String pid = " PID: " + getPID() + ", ";
try {
Files.write(Paths.get(logFile), (df.format(new Date()) + pid + msg + System.lineSeparator()).getBytes("UTF-8"),StandardOpenOption.CREATE,StandardOpenOption.APPEND);
if( ex != null ) {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bo);
ex.printStackTrace(ps);
Files.write(Paths.get(logFile), (df.format(new Date()) + pid + bo.toString() + System.lineSeparator()).getBytes("UTF-8"),StandardOpenOption.CREATE,StandardOpenOption.APPEND);
}
} catch (Exception e) {
//ignore
}
}
/*
* determin the current PID
*/
private static long getPID() {
try {
String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
return Long.parseLong(processName.split("@")[0]);
} catch( Exception ex ) {
return -999;
}
}
}
---------- END SOURCE ----------
FREQUENCY : always
Windows 7 64-Bit, JRE 10.0.2 (10.0.1)
A DESCRIPTION OF THE PROBLEM :
A Java main program (running with JRE-A e.g. 10.0.1, located at a/java) is starting a new Java process with an alternate JRE-B (e.g. 10.0.2 located at b/java) and terminates itself. The new process has still a handle to the modules file from JRE-A. Strangely, it has handles to both modules file in a/java/lib and b/java/lib.
REGRESSION : Last worked in version 8u181
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(1) create dir c:\temp\test\
(2) copy JRE 10.0.1 to c:\temp\test\a\java\
directories in c:\temp\test\:
c:\temp\test\a\java\release
c:\temp\test\a\java\README.html
c:\temp\test\a\java\bin\*
...
(3) copy JRE 10.0.2 to c:\temp\test\b\java\
additional directories in c:\temp\test\:
c:\temp\test\b\java\release
c:\temp\test\b\java\README.html
c:\temp\test\b\java\bin\*
...
(4) start cmd terminal window and cd to c:\temp\test
(5) set environment to JRE-A
> set JAVA_HOME=c:\temp\test\a\java
> set JRE_HOME=%JAVA_HOME%
> set PATH=%JAVA_HOME%\bin\;%PATH%
(6) copy TestRestartWithNewJre.class to c:\temp\test
(7) run the test program with parameter -newjre C:\temp\test\b\java
> java -cp . TestRestartWithNewJre -newjre C:\temp\test\b\java
(8) check the result:
(a) check p1.log: it shows the PID, the environment and properties of the starting process (JRE-A)
(b) check the task-manager: the starting process (with JRE-A) does not exist anymore
(c) check p2.log: it shows the PID, the environment and properties of the started process (JRE-B)
There is also an AccessDeniedException. This is thrown, because the renaming of the JRE-A home dir (a\java -> a\java-<timestramp>) failed.
(d) check the task-manager: the started process (with JRE-B) is still in process-list (sleeps for 1h)
(e) The Process-Explorer (procexp.exe, from Windows SysInternals) shows, that the started Java process (JRE-B) has handles to both modules file (from JRE-A and JRE-B)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The started process (using JRE-B) shouldn't have any reference to files in JRE-A
ACTUAL -
The Process-Explorer (procexp.exe, from Windows SysInternals) shows, that
(a) the first process (using JRE-A) does not exist anymore
(b) the started Java process (JRE-B) has handles to both modules file (from JRE-A and JRE-B)
---------- BEGIN SOURCE ----------
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestRestartWithNewJre {
private static String logFile = "p1.log";
private static String oldJreHome = null;
private static String newJreHome = null;
public static void main(String[] args) {
TestRestartWithNewJre t = new TestRestartWithNewJre();
try {
//get params
setParams(args);
//delete logfiles
new File( logFile ).delete();
//log params and all system properties
log("args: " + Arrays.asList(args));
dumpMap("properties: ", (Map) System.getProperties() );
dumpMap("env: ", System.getenv() );
// restart with alternate JRE or rename the old one
t.start();
} catch (IOException e) {
log("IOException found", e);
log("plese check first which file is locked..... (process sleep for 1h)");
try {
Thread.sleep(1000 * 60 * 60);
} catch (InterruptedException e1) {
log("sleep interrupted", e1);
}
} catch (Exception e) {
log("error", e);
}
}
private void start() throws IOException {
// if new jre home is given, restart this job with the new JRE and put the reference to the old JRE as parameter "-oldjre" (for renaming later on)
if( newJreHome != null ) {
File newJreHomeDir = new File(newJreHome);
if( newJreHomeDir.exists() && newJreHomeDir.isDirectory() ) {
List<String> cmdItems = new ArrayList<String>();
String javaExecutable = getJavaExe(newJreHomeDir);
cmdItems.add(javaExecutable);
cmdItems.add("-classpath");
String jarName = getJarName();
cmdItems.add(jarName);
cmdItems.add(this.getClass().getName());
cmdItems.add("-oldjre");
String jreHome = System.getProperty("java.home");
cmdItems.add(jreHome);
cmdItems.add("-logfile");
cmdItems.add("p2.log");
invokeNewProcess(newJreHome, cmdItems);
System.exit(1);
} else {
log("new JRE home '" + newJreHome + "' does not exist");
System.exit(1);
}
} else if( oldJreHome != null ) {
log("sleep for 2 seconds first, before trying to rename old/unused JRE-home");
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException e1) {
log("sleep interrupted", e1);
}
// if old/unused jre home is given, try to rename it
File oldJreHomeDir = new File(oldJreHome);
if( oldJreHomeDir.exists() && oldJreHomeDir.isDirectory() ) {
File targetPath = new File(oldJreHomeDir.getParentFile(), oldJreHomeDir.getName() + "-" + System.currentTimeMillis());
log("try to move '" + oldJreHome + "' to '" + targetPath.getAbsolutePath() + "'");
Path movedTargetPath = Files.move(oldJreHomeDir.toPath(), targetPath.toPath(), StandardCopyOption.ATOMIC_MOVE);
if( movedTargetPath.toFile().exists() ) {
log("rename '" + oldJreHome + "' to '" + movedTargetPath.toString() + "' done with success");
} else {
log("rename '" + oldJreHome + "' to '" + movedTargetPath.toString() + "' failed");
}
} else {
log("wrong parameter -oldjre '" + oldJreHome + "', path does not exist");
System.exit(1);
}
}
}
/*
* get params
*/
private static void setParams(String[] args) {
for( int i=0; i<args.length; i++ ) {
switch( args[i] ) {
case "-oldjre":
if( i+1 < args.length ) {
oldJreHome = args[++i];
}
break;
case "-newjre":
if( i+1 < args.length ) {
newJreHome = args[++i];
}
break;
case "-logfile":
if( i+1 < args.length ) {
logFile = args[++i];
}
break;
}
}
}
/*
* get the name of this jar (for restart with alternate jre)
*/
private String getJarName() {
return this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
}
/*
* determine the path/name of the java executable for the given JRE home (the alternate jre for restart !)
*/
private String getJavaExe(final File javaHomeDir) throws IOException {
final Path start = javaHomeDir.toPath();
final int maxDepth = 5;
final StringBuilder sb = new StringBuilder("");
try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) -> ((String.valueOf(path).endsWith("java.exe")||String.valueOf(path).endsWith("java"))&&path.toFile().isFile()&&(path.toFile().canExecute())))) {
sb.append( stream.sorted().map(String::valueOf).collect(Collectors.joining(";")) );
}
String javaExe[] = sb.toString().split(";");
if( javaExe.length != 1 ) {
throw new RuntimeException("java executable not found: " + Arrays.asList(javaExe));
}
File javaExeFile = new File(javaExe[0]);
boolean javaExecutableFound = javaExeFile != null && javaExeFile.exists() && javaExeFile.isFile() && javaExeFile.canRead() && javaExeFile.canExecute();
if( !javaExecutableFound ) {
throw new RuntimeException("java file not executable: " + javaExeFile.getAbsolutePath());
}
log("getJavaExe: " + javaExeFile.getCanonicalPath() );
return javaExeFile.getCanonicalPath();
}
/*
* invoke the new process (with alternate JRE)
*/
private void invokeNewProcess(String newJavaHome, List<String> cmdItems) throws IOException {
log("try to start process: " + cmdItems);
final ProcessBuilder pb = new ProcessBuilder(cmdItems);
Map<String,String> pbEnv = pb.environment();
if( newJavaHome != null ) {
pbEnv.clear();
pbEnv.put("JRE_HOME", newJavaHome);
pbEnv.put("JAVA_HOME", newJavaHome);
}
dumpMap("env for new process: ", pbEnv);
pb.redirectErrorStream(true);
pb.redirectInput();
final Process process = pb.start();
log("process is started: " + process.isAlive());
}
/*
* log the current map (env or system properties)
*/
private static void dumpMap(final String label, final Map<String,String> map) {
Set<String> keys = map.keySet();
java.util.List<String> sortedKeys = new ArrayList<String>(keys);
Collections.sort(sortedKeys);
StringBuilder buf = new StringBuilder(label).append(System.lineSeparator());
for (Iterator<String> iter = sortedKeys.iterator(); iter.hasNext();) {
String key = iter.next();
String value = map.get(key);
buf.append("\t" + key + "=" + value).append(System.lineSeparator());
}
log(buf.toString());
}
/*
* log msg
*/
private static void log(String msg) {
log(msg, null);
}
/*
* log msg and exception (if not null)
*/
private static void log(String msg, Exception ex ) {
final DateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss SSS ");
String pid = " PID: " + getPID() + ", ";
try {
Files.write(Paths.get(logFile), (df.format(new Date()) + pid + msg + System.lineSeparator()).getBytes("UTF-8"),StandardOpenOption.CREATE,StandardOpenOption.APPEND);
if( ex != null ) {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bo);
ex.printStackTrace(ps);
Files.write(Paths.get(logFile), (df.format(new Date()) + pid + bo.toString() + System.lineSeparator()).getBytes("UTF-8"),StandardOpenOption.CREATE,StandardOpenOption.APPEND);
}
} catch (Exception e) {
//ignore
}
}
/*
* determin the current PID
*/
private static long getPID() {
try {
String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
return Long.parseLong(processName.split("@")[0]);
} catch( Exception ex ) {
return -999;
}
}
}
---------- END SOURCE ----------
FREQUENCY : always
- duplicates
-
JDK-8194734 Handle to jimage file inherited into child processes (win)
- Closed