[Einführung] String intern in Java 6,7,8 – In diesem Artikel wird erläutert, wie die String-intern-Methode in Java 6 implementiert wird und wie diese Methode in Java 7 funktioniert und was In Java 8 wurden Anpassungen vorgenommen. String String String Pool (standardisiert als Namensstring)
Dieser Parameter ist in Java 6 keine große Hilfe, da Sie immer noch auf die feste Permgen-Speichergröße beschränkt sind. In der folgenden Diskussion wird Java 6 direkt ignoriert
Sie müssen einen größeren -XX:StringTalbeSize
-Wert festlegen (im Vergleich zum Standardwert 1009), wenn Sie String.intern( ) verwenden möchten – andernfalls diese Methode wird es schnell auf 0 (Poolgröße) reduzieren.
In diesem Artikel wird erläutert, wie die String.intern
-Methode in Java 6 implementiert wird und wie diese Methode implementiert wird Welche Anpassungen wurden in Java 7 und Java 8 vorgenommen?
String-Pooling (benannte String-Normalisierung) wird durchgeführt, indem ein eindeutiges gemeinsames String
Objekt verwendet wird, um Zeichen darzustellen, die denselben Wert, aber unterschiedliche Adressstrings verwenden Verfahren. Sie können Ihre eigene definierte <a href="http://www.php.cn/code/8210.html" target="_blank">Map<code><a href="http://www.php.cn/code/8210.html" target="_blank">Map</a>05ad6303f369fc4ccec4412db2772d19
05ad6303f369fc4ccec4412db2772d19 verwenden (Verwenden Sie je nach Bedarf eine schwache -Referenz oder eine weiche Referenz) und verwenden Sie den Wert in der Karte als Standardwert, um dieses Ziel zu erreichen, oder Sie können auch die vom JDK bereitgestellte String.intern()
verwenden.
Viele Standards verbieten die Verwendung in Java 6String.intern()
Denn wenn der Pool häufig verwendet wird, wird er von der Stadt kontrolliert und es besteht eine hohe Wahrscheinlichkeit, dass er OutOfMemory<a href="ausl%C3%B6st%20http://www.php.cn/wiki/265.html" target="_blank">Ausnahme<code>OutOfMemory<a href="http://www.php.cn/wiki/265.html" target="_blank">Exception</a>
. Oracle Java 7 hat viele Verbesserungen am String-Pool vorgenommen. Weitere Informationen finden Sie unter bugs.sun.com/view_bug.do?bug_id=6962931 und bugs.sun.com/view_bug.do?bug_id=6962930
In den guten alten Zeiten wurden alle gemeinsam genutzten String-Objekte in PermGen gespeichert – einem Teil des Heaps mit fester Größe, der hauptsächlich zum Speichern geladener Klassenobjekte und String-Pools verwendet wurde. Zusätzlich zu explizit gemeinsam genutzten Zeichenfolgen enthält der PermGen-String-Pool auch Zeichenfolgen, die in allen Programmen verwendet werden (beachten Sie hier, dass Zeichenfolgen verwendet werden. Wenn die Klasse oder Methode nie geladen oder verwendet wird, werden keine Konstanten geladen).
Das größte Problem mit dem String-Pool in Java 6 ist sein Speicherort – PermGen. Die Größe von PermGen ist fest und kann zur Laufzeit nicht erweitert werden. Sie können die Größe mithilfe der -XX:MaxPermSize=N
-Konfiguration ändern. Soweit ich weiß, liegt die Standardgröße von PermGen für verschiedene Plattformen zwischen 32 MB und 96 MB. Sie können die Größe erweitern, die Größenverwendung ist jedoch festgelegt. Diese Einschränkung erfordert, dass Sie bei der Verwendung von String.intern
sehr vorsichtig sein müssen – Sie sollten diese Methode besser nicht verwenden, um unkontrollierbare Benutzereingaben zu internieren. Aus diesem Grund wird in JAVA6 hauptsächlich die manuelle Verwaltung verwendet Map
, um den String-Pool
Oracle-Ingenieure haben in Java 7 einen String-Pool. Die Logik war stark verändert – der Speicherort des String-Pools wurde an den Heap angepasst. Dadurch sind Sie nicht mehr an einen festen Speicherplatz gebunden. Alle Zeichenfolgen werden wie andere gewöhnliche Objekte im Heap gespeichert, sodass Sie die Heap-Größe nur beim Optimieren Ihrer Anwendung anpassen können. Diese Änderung gibt uns Anlass genug, die Verwendung von String.intern() in Java 7 zu überdenken.
Ja, alle Strings im JVM-String-Pool werden durch Garbage Collection erfasst, wenn diese Werte keine Referenzen in der Anwendung haben . Dies wird in allen Java-Versionen verwendet, was bedeutet, dass, wenn die interne Zeichenfolge außerhalb des Gültigkeitsbereichs liegt und keine Referenz hat, sie aus dem Zeichenfolgenpool der JVM durch Müll gesammelt wird.
Da es auf den Heap verschoben und durch Müll gesammelt wird, scheint der String-Pool der JVM ein geeigneter Ort zum Speichern von Strings zu sein, oder? Theoretisch werden Zeichenfolgen, die gegen die Verwendung verstoßen, aus dem Pool gesammelt, was Speicherplatz sparen kann, wenn ein Zeichen von außen eingegeben wird und im Pool vorhanden ist. Sieht nach einer perfekten Strategie zum Speichersparen aus? Bevor Sie diese Frage beantworten, müssen Sie unbedingt wissen, wie String-Pooling implementiert wird.
Der String-Pool verwendet eine feste KapazitätHashMap
Jedes Element enthält einen String mit derselben Hashwertliste. Einige Implementierungsdetails können dem Java-Fehlerbericht bugs.sun.com/view_bug.do?bug_id=6962930
Die Standard-Poolgröße beträgt 1009 (erscheint im Quellcode des oben erwähnten Fehlerberichts und wurde in Java7u40 erhöht). Dies war eine Konstante in frühen Versionen von JAVA 6 und wurde später angepasst, um in java6u30 bis java6u41 konfigurierbar zu sein. In Java 7 ist es von Anfang an konfigurierbar (zumindest ist es in Java7u02 konfigurierbar). Sie müssen den Parameter -XX:StringTableSize=N
angeben. N ist die Größe des String-Pools Map
. Stellen Sie sicher, dass es sich um eine voreingestellte Größe zur Leistungsoptimierung handelt.
In Java 6 ist dieser Parameter keine große Hilfe, da Sie immer noch auf die feste PermGen-Speichergröße beschränkt sind. In der folgenden Diskussion wird Java 6 direkt ignoriert
Mit anderen Worten, in Java7 sind Sie auf eine größere Heap-Speichermitte beschränkt. Dies bedeutet, dass Sie die Größe des String-Pools voreinstellen können (dieser Wert hängt von den Anforderungen Ihrer Anwendung ab). Im Allgemeinen wächst der Speicher um Hunderte von Megabyte, sobald ein Programm beginnt. In diesem Fall scheint es angemessener zu sein, einem String-Pool mit 1 Million String-Objekten 8-16 MB Speicher zuzuweisen (Verwenden Sie nicht 1.000.000 als). der Wert von -XX:StringTaleSize
– es ist keine Primzahl; verwenden Sie stattdessen 1,000,003
Mir ist die Abhängigkeit beim Internieren von Zeichenfolgen mit weniger als 100 Zeichen nicht aufgefallen (ich glaube nicht, dass eine Zeichenfolge mit 50 wiederholten Zeichen realen Daten ähnelt, also scheinen 100 Zeichen zu sein (wie eine gute Grenze zum Testen) Hier sind die Anwendungsprotokolle für die Standard-Poolgröße: Die erste Spalte ist die Anzahl der Zeichenfolgen, die interniert wurden, die zweite Spalte enthält ständig 10.000 Zeichenfolgen (Sekunden)Sie müssen einen größeren -Wert festlegen (im Vergleich zum Standardwert 1009), wenn Sie String.intern() häufiger verwenden möchten. Andernfalls wird diese Methode sehr schnell verringert 0 (Poolgröße).
-XX:StringTalbeSize
0; time = 0.0 sec 50000; time = 0.03 sec 100000; time = 0.073 sec 150000; time = 0.13 sec 200000; time = 0.196 sec 250000; time = 0.279 sec 300000; time = 0.376 sec 350000; time = 0.471 sec 400000; time = 0.574 sec 450000; time = 0.666 sec 500000; time = 0.755 sec 550000; time = 0.854 sec 600000; time = 0.916 sec 650000; time = 1.006 sec 700000; time = 1.095 sec 750000; time = 1.273 sec 800000; time = 1.248 sec 850000; time = 1.446 sec 900000; time = 1.585 sec 950000; time = 1.635 sec 1000000; time = 1.913 secDie Tests wurden auf einem Core i5-3317U@1,7-GHz-CPU-Gerät durchgeführt. Sie können sehen, dass es linear wächst, und da der JVM-String-Pool eine Million Strings enthält, kann ich immer noch etwa 5000 Strings pro Sekunde internieren, was für eine Anwendung, die viele Daten im Speicher verarbeitet, gut ist. Zu langsam. Passen Sie nun die
-Parameter an, um den Test erneut auszuführen: -XX:StringTableSize=100003
50000; time = 0.017 sec 100000; time = 0.009 sec 150000; time = 0.01 sec 200000; time = 0.009 sec 250000; time = 0.007 sec 300000; time = 0.008 sec 350000; time = 0.009 sec 400000; time = 0.009 sec 450000; time = 0.01 sec 500000; time = 0.013 sec 550000; time = 0.011 sec 600000; time = 0.012 sec 650000; time = 0.015 sec 700000; time = 0.015 sec 750000; time = 0.01 sec 800000; time = 0.01 sec 850000; time = 0.011 sec 900000; time = 0.011 sec 950000; time = 0.012 sec 1000000; time = 0.012 secWie Sie sehen können, ist die Zeit zum Einfügen der Zeichenfolge ungefähr konstant (gemittelt über die Liste). Die Anzahl der Zeichenfolgen in der Karte überschreitet 10 nicht. Unten sehen Sie das Ergebnis des gleichen Setups, aber diesmal werden wir 10 Millionen Zeichenfolgen in den Pool einfügen (dies bedeutet, dass die Zeichenfolgenliste in der Karte durchschnittlich 100 Zeichenfolgen enthält). )
2000000; time = 0.024 sec 3000000; time = 0.028 sec 4000000; time = 0.053 sec 5000000; time = 0.051 sec 6000000; time = 0.034 sec 7000000; time = 0.041 sec 8000000; time = 0.089 sec 9000000; time = 0.111 sec 10000000; time = 0.123 secJetzt erhöhen wir die Essensgröße auf 1 Million (1.000.003 um genau zu sein)
1000000; time = 0.005 sec 2000000; time = 0.005 sec 3000000; time = 0.005 sec 4000000; time = 0.004 sec 5000000; time = 0.004 sec 6000000; time = 0.009 sec 7000000; time = 0.01 sec 8000000; time = 0.009 sec 9000000; time = 0.009 sec 10000000; time = 0.009 secWie Sie sehen können, sind die Zeiten sehr gleichmäßig und konsistent mit „0 bis 1 Million“-Uhr ist nicht viel anders. Selbst bei einem ausreichend großen Pool fügt mein Notebook 1.000.000 Zeichenobjekte pro Sekunde hinzu. Müssen wir den String-Pool immer noch manuell verwalten? Jetzt müssen wir den JVM-String-Pool und
vergleichen, der zur Simulation des JVM-String-Pools verwendet werden kann. Die folgende Methode wird verwendet, um WeakHashMap5e31d6cc56a10347ef5ebe29e4b04285>
zu ersetzen: String.intern
private static final WeakHashMap<String, WeakReference<String>> s_manualCache = new WeakHashMap<String, WeakReference<String>>( 100000 ); private static String manualIntern( final String str ) { final WeakReference<String> cached = s_manualCache.get( str ); if ( cached != null ) { final String value = cached.get(); if ( value != null ) return value; } s_manualCache.put( str, new WeakReference<String>( str ) ); return str; }Derselbe Test unten für handgeschriebene Pools:
0; manual time = 0.001 sec 50000; manual time = 0.03 sec 100000; manual time = 0.034 sec 150000; manual time = 0.008 sec 200000; manual time = 0.019 sec 250000; manual time = 0.011 sec 300000; manual time = 0.011 sec 350000; manual time = 0.008 sec 400000; manual time = 0.027 sec 450000; manual time = 0.008 sec 500000; manual time = 0.009 sec 550000; manual time = 0.008 sec 600000; manual time = 0.008 sec 650000; manual time = 0.008 sec 700000; manual time = 0.008 sec 750000; manual time = 0.011 sec 800000; manual time = 0.007 sec 850000; manual time = 0.008 sec 900000; manual time = 0.008 sec 950000; manual time = 0.008 sec 1000000; manual time = 0.008 secHandgeschriebene Pools bieten eine gute Leistung, wenn die JVM dies getan hat genügend Speicherleistung. Leider behält mein Test (unter Beibehaltung von
) sehr kurze Zeichenfolgen bei und ermöglichte mir, 2,5 Millionen solcher Zeichenfolgen beizubehalten, wenn ich den Parameter String.valueOf(0 < N < 1,000,000,000)
verwendete. Der JVM-String-Pool (Größe = 1.000.003) bietet dagegen die gleichen Leistungsmerkmale, wenn der JVM-Speicher ausreichend ist, bis der JVM-String-Pool 12,72 Millionen Strings enthält und den gesamten Speicher verbraucht (fünfmal mehr). Meiner Meinung nach lohnt es sich, das gesamte manuelle String-Pooling aus Ihrer Anwendung zu entfernen. -Xmx1280M
erhalten. -XX:+PrintFlagsFinal
unterstützt, um mit Java 7-Funktionen kompatibel zu sein. Der Hauptunterschied besteht darin, dass die Standardpoolgröße in Java 8 auf 60013 erhöht wurde: -XX:StringTableSize
50000; time = 0.019 sec 100000; time = 0.009 sec 150000; time = 0.009 sec 200000; time = 0.009 sec 250000; time = 0.009 sec 300000; time = 0.009 sec 350000; time = 0.011 sec 400000; time = 0.012 sec 450000; time = 0.01 sec 500000; time = 0.013 sec 550000; time = 0.013 sec 600000; time = 0.014 sec 650000; time = 0.018 sec 700000; time = 0.015 sec 750000; time = 0.029 sec 800000; time = 0.018 sec 850000; time = 0.02 sec 900000; time = 0.017 sec 950000; time = 0.018 sec 1000000; time = 0.021 secTestcodeDer Testcode für diesen Artikel ist sehr einfach und erstellt und behält neue Zeichen bei in einer Schleife in einer Methodenzeichenfolge. Sie können messen, wie lange es dauert, 10.000 Zeichenfolgen aufzubewahren. Führen Sie diesen Test am besten mit den
JVM-Parametern aus, um zu sehen, wann und wie die Garbage Collection erfolgt. Außerdem ist es besser, den Parameter -verbose:gc
zu verwenden, um die maximale Größe des Heaps zu erzwingen. -Xmx
这里有两个测试:testStringPoolGarbageCollection
将显示 JVM 字符串池被垃圾收集 — 检查垃圾收集日志消息。在 Java 6 的默认 PermGen 大小配置上,这个测试会失败,因此最好增加这个值,或者更新测试方法,或者使用 Java 7.
第二个测试显示内存中保留了多少字符串。在 Java 6 中执行需要两个不同的内存配置 比如: -Xmx128M
以及 -Xmx1280M
(10 倍以上)。你可能发现这个值不会影响放入池中字符串的数量。另一方面,在 Java 7 中你能够在堆中填满你的字符串。
/** - Testing String.intern. * - Run this class at least with -verbose:gc JVM parameter. */ public class InternTest { public static void main( String[] args ) { testStringPoolGarbageCollection(); testLongLoop(); } /** - Use this method to see where interned strings are stored - and how many of them can you fit for the given heap size. */ private static void testLongLoop() { test( 1000 * 1000 * 1000 ); //uncomment the following line to see the hand-written cache performance //testManual( 1000 * 1000 * 1000 ); } /** - Use this method to check that not used interned strings are garbage collected. */ private static void testStringPoolGarbageCollection() { //first method call - use it as a reference test( 1000 * 1000 ); //we are going to clean the cache here. System.gc(); //check the memory consumption and how long does it take to intern strings //in the second method call. test( 1000 * 1000 ); } private static void test( final int cnt ) { final List<String> lst = new ArrayList<String>( 100 ); long start = System.currentTimeMillis(); for ( int i = 0; i < cnt; ++i ) { final String str = "Very long test string, which tells you about something " + "very-very important, definitely deserving to be interned #" + i; //uncomment the following line to test dependency from string length // final String str = Integer.toString( i ); lst.add( str.intern() ); if ( i % 10000 == 0 ) { System.out.println( i + "; time = " + ( System.currentTimeMillis() - start ) / 1000.0 + " sec" ); start = System.currentTimeMillis(); } } System.out.println( "Total length = " + lst.size() ); } private static final WeakHashMap<String, WeakReference<String>> s_manualCache = new WeakHashMap<String, WeakReference<String>>( 100000 ); private static String manualIntern( final String str ) { final WeakReference<String> cached = s_manualCache.get( str ); if ( cached != null ) { final String value = cached.get(); if ( value != null ) return value; } s_manualCache.put( str, new WeakReference<String>( str ) ); return str; } private static void testManual( final int cnt ) { final List<String> lst = new ArrayList<String>( 100 ); long start = System.currentTimeMillis(); for ( int i = 0; i < cnt; ++i ) { final String str = "Very long test string, which tells you about something " + "very-very important, definitely deserving to be interned #" + i; lst.add( manualIntern( str ) ); if ( i % 10000 == 0 ) { System.out.println( i + "; manual time = " + ( System.currentTimeMillis() - start ) / 1000.0 + " sec" ); start = System.currentTimeMillis(); } } System.out.println( "Total length = " + lst.size() ); } }
由于 Java 6 中使用固定的内存大小(PermGen)因此不要使用 String.intern()
方法。
Java7 和 8 在堆内存中实现字符串池。这以为这字符串池的内存限制等于应用程序的内存限制。
在 Java 7 和 8 中使用 -XX:StringTableSize
来设置字符串池 Map 的大小。它是固定的,因为它使用 HashMap
实现。近似于你应用单独的字符串个数(你希望保留的)并且设置池的大小为最接近的质数并乘以 2 (减少碰撞的可能性)。它是的 String.intern
可以使用相同(固定)的时间并且在每次插入时消耗更小的内存(同样的任务,使用java WeakHashMap将消耗4-5倍的内存)。
在 Java 6 和 7(Java7u40以前) 中 -XX:StringTableSize
参数的值是 1009。Java7u40 以后这个值调整为 60013 (Java 8 中使用相同的值)。
如果你不确定字符串池的用量,参考:-XX:+PrintStringTableStatistics
JVM 参数,当你的应用挂掉时它告诉你字符串池的使用量信息。
Das obige ist der detaillierte Inhalt vonTeilen verschiedener Techniken zur Verbesserung der Java-Codeleistung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!