J'ai fait un test à ce sujet. Voici les pièces que j'ai fait:
- Obtenir le code source d'
String.java
de JDK.
-
Modifier son toCharArray
méthode Arrays.copyOf
.
comme ceci:
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
/*char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;*/
return Arrays.copyOf(value, value.length);
}
compiler ce modifiés String
, et l'enregistrer de nouveau dans JRE rt.jar.
Écrire une simple HelloWorld code Java.
Compiler et exécuter le code à l'aide d' java
programme.
Et enfin, j'obtiens ceci:
PS D:\> & 'C:\Program Files (x86)\Java\jdk1.8.0_121\jre\bin\java.exe' StringTest
Error occurred during initialization of VM
java.lang.NullPointerException
at java.util.Hashtable.remove(Hashtable.java:491)
at java.lang.System.initProperties(Native Method)
at java.lang.System.initializeSystemClass(System.java:1166)
Nous pouvons voir qu'il y a vraiment un initialization
d'erreur. Et parce qu' System.initProperties
est une méthode native, je ne peux pas vérifier son code.
Cependant, Nous pouvons faire deviner pourquoi cela pourrait se produire:
-
System.initProperties
doit gérer certaines Chaînes tandis qu'il initialise les propriétés du système.
- Et tout en faisant initialiser, il pourrait invoquer
String.toCharArray
pour obtenir des tableaux de char de ces chaînes.
- Les chaînes invoquer
Arrays.copyOf
, mais à ce point, & cette fois, Arrays
n'a pas été chargé / initialisé.
- Différentes de l'exécution du code Java, le code natif n'en demandait pas un
class initializing request
(je ne suis pas sûr à ce sujet, s'il vous plaît laissez-moi savoir si je me trompe!!), et qui va conduire à un NullPointerException
et de faire des VM de sortie.
2018.04.10 mise à Jour.
Je tiens à Remercier @Radiodef pour son astuce. Mais quand j'ai essayé d'aller dans le C++ codes, j'ai été arrêté par un si grand nombre de chemins d'exécution qui je ne pouvais pas gérer sans courir le débogage ou de l'OpenJDK de la JVM.
Et puis, j'ai changé ma stratégie. J'ai fait un peu plus de test basée sur l'au-dessus de laquelle je l'avais fait pour quelques jours.
Cette fois, je ne vais pas utiliser Arrays.copyOf
avec String.toCharArray
, au lieu de cela, je tente de trouver lequel les codes de l'invoquer toCharArray
méthode alors que la JVM en cours d'initialisation.
J'ai donc modifier String
, ajouter deux variables statiques pour elle:
public static int count;
public static Throwable[] logs = new Throwable[10000];
En ce qui count
est utilisé pour compter de l'invocation de l' toCharArray
, logs
est utilisé pour garder ceux invocation de traces de pile.
En toCharArray
méthode:
public char[] toCharArray() {
if (count < logs.length) {
try {
throw new RuntimeException();
} catch (Throwable e) {
logs[count] = e;
}
}
count++;
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
Après avoir fait ces, je compile String
nouveau et de les enregistrer en arrière dans rt.jar.
Ensuite, j'écris un programme de test pour imprimer count
et l'invocation des traces de pile en sortie:
Class<String> clazz = String.class;
Field count = clazz.getDeclaredField("count");
System.out.println(count.getInt(null));
Field logs = clazz.getDeclaredField("logs");
Throwable[] arr = (Throwable[]) logs.get(null);
for (Throwable e : arr) {
if (e != null)
e.printStackTrace(System.out);
}
Nous ne pouvons pas accéder String.count & String.logs
directement dans nos codes, comme compilateur (javac
) ne reconnaît pas ces champs et provoquera une erreur de compilation. C'est pourquoi je suis à l'aide de réfléchir façon de le faire.
Exécutez le programme que nous avons réalisé, et les résultats seront les suivants:
525
java.lang.RuntimeException
at java.lang.String.toCharArray(String.java:2889)
at sun.nio.cs.ext.GBK.initb2c(Unknown Source)
at sun.nio.cs.ext.GBK.newDecoder(Unknown Source)
at java.lang.StringCoding$StringDecoder.<init>(Unknown Source)
at java.lang.StringCoding$StringDecoder.<init>(Unknown Source)
at java.lang.StringCoding.decode(Unknown Source)
at java.lang.String.<init>(String.java:414)
at java.lang.String.<init>(String.java:479)
at java.lang.System.initProperties(Native Method)
at java.lang.System.initializeSystemClass(Unknown Source)
......
java.lang.RuntimeException
at java.lang.String.toCharArray(String.java:2889)
at sun.net.www.ParseUtil.encodePath(Unknown Source)
at sun.misc.URLClassPath$FileLoader.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Ce qu'une longue liste d'invocation. Cependant il est plus clair que le test précédent. Nous pouvons clairement voir les classes invoquer toCharArray
.