当前位置: 首页 > wzjs >正文

金华哪里做网站设计北京

金华哪里做网站,设计北京,济南手机建站模板,活动策划怎么写问题 String.intern()的工作原理?我们应该如何使用它? 基础知识 字符串池(String Pool) String类在我们日常编程工作中是使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维…

问题

String.intern()的工作原理?我们应该如何使用它?

基础知识

字符串池(String Pool)

String类在我们日常编程工作中是使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间,即字符串池(String Pool)。字符串池由String类私有的维护。

字符串驻留(String Interning)

VM(Java虚拟机)在运行时将字符串常量或字符串对象存储在一个特殊的池(String Pool)中,以便重用相同的字符串对象,而不是为每个相同的字符串分配新的内存空间。这样可以减少内存的使用,提高效率,特别是在处理大量字符串时。

自动驻留(String Literal Pooling)

当你使用双引号直接定义字符串常量时,JVM会自动将这个字符串加入到字符串池中。如果池中已经存在相同的字符串,JVM会返回池中已存在的那个引用,而不是创建一个新的字符串对象。

手动驻留(String.intern() 方法)

如果你有一个字符串对象,但希望它被加入到字符串池中(即使它不是通过字面量直接创建的),你可以使用String类的intern()方法。

intern()

public String intern()

当调用 intern 方法时,如果池中已经包含一个由 equals(Object) 方法确定的与此 String 对象相等的字符串,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对此 String 对象的引用。— JDK Javadoc

这样设计本质就是为了优化内存的使用,然而这样做却也有缺点:在 OpenJDK 中, String.intern()是本地方法的,它实际上调用 JVM,将 String 驻留在本地 JVM String 池中。这是因为当本地 VM 和 JDK 代码都必须就特定 String 对象的身份达成一致时,String 驻留是 JDK-VM 接口的一部分。那这么做有什么意义呢?其意义如下:

  • 每次intern()时都需要跨 JDK-JVM 接口,这会浪费周期。
  • 性能取决于本地的HashTable 实现,这可能会落后于高性能 Java 领域中的实现,尤其是在并发访问的情况下。
  • 由于 Java 字符串是来自本地 VM 结构的引用,因此它们成为 GC 根集的一部分。在许多情况下,这需要在 GC 暂停期间进行额外的工作来处理。

实验

大量字符串场景下的吞吐量。

源码-字符串驻留用例

@State(Scope.Benchmark)
public class StringIntern {@Param({"1", "100", "10000", "1000000"})private int size;private StringInterner str;private CHMInterner chm;private HMInterner hm;@Setuppublic void setup() {str = new StringInterner();chm = new CHMInterner();hm = new HMInterner();}public static class StringInterner {public String intern(String s) {return s.intern();}}@Benchmarkpublic void intern(Blackhole bh) {for (int c = 0; c < size; c++) {bh.consume(str.intern("String" + c));}}public static class CHMInterner {private final Map<String, String> map;public CHMInterner() {map = new ConcurrentHashMap<>();}public String intern(String s) {String exist = map.putIfAbsent(s, s);return (exist == null) ? s : exist;}}@Benchmarkpublic void chm(Blackhole bh) {for (int c = 0; c < size; c++) {bh.consume(chm.intern("String" + c));}}public static class HMInterner {private final Map<String, String> map;public HMInterner() {map = new HashMap<>();}public String intern(String s) {String exist = map.putIfAbsent(s, s);return (exist == null) ? s : exist;}}@Benchmarkpublic void hm(Blackhole bh) {for (int c = 0; c < size; c++) {bh.consume(hm.intern("String" + c));}}
}

运行结果

Benchmark             (size)  Mode  Cnt       Score       Error  UnitsStringIntern.chm           1  avgt   25       0.038 ±     0.001  us/op
StringIntern.chm         100  avgt   25       4.030 ±     0.013  us/op
StringIntern.chm       10000  avgt   25     516.483 ±     3.638  us/op
StringIntern.chm     1000000  avgt   25   93588.623 ±  4838.265  us/opStringIntern.hm            1  avgt   25       0.028 ±     0.001  us/op
StringIntern.hm          100  avgt   25       2.982 ±     0.073  us/op
StringIntern.hm        10000  avgt   25     422.782 ±     1.960  us/op
StringIntern.hm      1000000  avgt   25   81194.779 ±  4905.934  us/opStringIntern.intern        1  avgt   25       0.089 ±     0.001  us/op
StringIntern.intern      100  avgt   25       9.324 ±     0.096  us/op
StringIntern.intern    10000  avgt   25    1196.700 ±   141.915  us/op
StringIntern.intern  1000000  avgt   25  650243.474 ± 36680.057  us/op

由上述运行结果可知,伴随着测试字符串数量的增大,String.intern()变得越来越慢,这和我们之前期望的提高效率背道而驰。
通过perf record -g进一步查看结果,如下:

-    6.63%     0.00%  java     [unknown]           [k] 0x00000006f8000041- 0x6f8000041- 6.41% 0x7faedd1ee354- 6.41% 0x7faedd170426- JVM_InternString- 5.82% StringTable::intern- 4.85% StringTable::intern0.39% java_lang_String::equals0.19% Monitor::lock+ 0.00% StringTable::basic_add- 0.97% java_lang_String::as_unicode_stringresource_allocate_bytes0.19% JNIHandleBlock::allocate_handle0.19% JNIHandles::make_local

由上述时间占比可知,在StringTable相关的处理逻辑占用的时间尤其明显,而通过-XX:+PrintStringTableStatistics进一步查看结果,如下:

StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :   1002714 =  24065136 bytes, avg  24.000
Number of literals      :   1002714 =  64192616 bytes, avg  64.019
Total footprint         :           =  88737856 bytes
Average bucket size     :    16.708  ; <---- !!!!!!

由上述结果可知,链式哈希表中每个桶 16 个元素意味着严重过载了。更糟糕的是,该字符串表不可调整大小,但是也可以通过-XX:StringTableSize来设置StringTable的默认大小,比如设置为10M,重新运行结果如下:

Benchmark             (size)  Mode  Cnt       Score       Error  Units# Default, copied from above
StringIntern.chm           1  avgt   25       0.038 ±     0.001  us/op
StringIntern.chm         100  avgt   25       4.030 ±     0.013  us/op
StringIntern.chm       10000  avgt   25     516.483 ±     3.638  us/op
StringIntern.chm     1000000  avgt   25   93588.623 ±  4838.265  us/op# Default, copied from above
StringIntern.intern        1  avgt   25       0.089 ±     0.001  us/op
StringIntern.intern      100  avgt   25       9.324 ±     0.096  us/op
StringIntern.intern    10000  avgt   25    1196.700 ±   141.915  us/op
StringIntern.intern  1000000  avgt   25  650243.474 ± 36680.057  us/op# StringTableSize = 10M
StringIntern.intern        1  avgt    5       0.097 ±     0.041  us/op
StringIntern.intern      100  avgt    5      10.174 ±     5.026  us/op
StringIntern.intern    10000  avgt    5    1152.387 ±   558.044  us/op
StringIntern.intern  1000000  avgt    5  130862.190 ± 61200.783  us/op

通过上述运行结果的对比,可发现当将StringTableSize设置为10M时,性能有一定程度的提升,而这种处理方式需要依据实际的预估字符串的规模,机器的内存等资源情况,进行合理的规划和设计,并且盲目地将 StringTableSize 表大小设置为大值,并且不使用它,你将浪费内存。即使你充分利用了大型 StringTable,本地调用成本仍然会消耗周期。

垃圾回收停顿

原生 StringTable最显著的特点是它是 GC 根的一部分,它应该由垃圾收集器专门扫描/更新。在 OpenJDK 中,这意味着在暂停期间要进行艰苦的工作。事实上,对于Shenandoah来说,暂停主要取决于 GC 根集大小,StringTable中只有 1M 条记录会产生以下结果:

$ ... StringIntern -p size=1000000 --jvmArgs "-XX:+UseShenandoahGC -Xlog:gc+stats -Xmx1g -Xms1g"
...
Initial Mark Pauses (G)    = 0.03 s (a = 15667 us) (n = 2) (lvls, us = 15039, 15039, 15039, 15039, 16260)
Initial Mark Pauses (N)    = 0.03 s (a = 15516 us) (n = 2) (lvls, us = 14844, 14844, 14844, 14844, 16088)Scan Roots               = 0.03 s (a = 15448 us) (n = 2) (lvls, us = 14844, 14844, 14844, 14844, 16018)S: Thread Roots        = 0.00 s (a =    64 us) (n = 2) (lvls, us =    41,    41,    41,    41,    87)S: String Table Roots  = 0.03 s (a = 13210 us) (n = 2) (lvls, us = 12695, 12695, 12695, 12695, 13544)S: Universe Roots      = 0.00 s (a =     2 us) (n = 2) (lvls, us =     2,     2,     2,     2,     2)S: JNI Roots           = 0.00 s (a =     3 us) (n = 2) (lvls, us =     2,     2,     2,     2,     4)S: JNI Weak Roots      = 0.00 s (a =    35 us) (n = 2) (lvls, us =    29,    29,    29,    29,    42)S: Synchronizer Roots  = 0.00 s (a =     0 us) (n = 2) (lvls, us =     0,     0,     0,     0,     0)S: Flat Profiler Roots = 0.00 s (a =     0 us) (n = 2) (lvls, us =     0,     0,     0,     0,     0)S: Management Roots    = 0.00 s (a =     1 us) (n = 2) (lvls, us =     1,     1,     1,     1,     1)S: System Dict Roots   = 0.00 s (a =     9 us) (n = 2) (lvls, us =     8,     8,     8,     8,    11)S: CLDG Roots          = 0.00 s (a =    75 us) (n = 2) (lvls, us =    68,    68,    68,    68,    81)S: JVMTI Roots         = 0.00 s (a =     0 us) (n = 2) (lvls, us =     0,     0,     0,     0,     1)

如上述的String Table Roots 所示,由于我们决定在根集中放入更多内容,因此每次暂停就会增加 +13 毫秒左右。这促使一些 GC 实现仅在执行繁重工作时才执行StringTable清理。例如,从 JVM 的角度来看,如果类未卸载,则清理StringTable毫无意义 — — 因为已加载的类是驻留字符串的主要来源。因此,这种工作负载至少在 G1 和 CMS 中会表现出有趣的行为,以下面的测试用例为例:

public class InternMuch {public static void main(String... args) {for (int c = 0; c < 1_000_000_000; c++) {String s = "" + c + "root";s.intern();}}
}

运行结果

$ java -XX:+UseConcMarkSweepGC -Xmx2g -Xms2g -verbose:gc -XX:StringTableSize=6661443 InternMuchGC(7) Pause Young (Allocation Failure) 349M->349M(989M) 357.485ms
GC(8) Pause Initial Mark 354M->354M(989M) 3.605ms
GC(8) Concurrent Mark
GC(8) Concurrent Mark 1.711ms
GC(8) Concurrent Preclean
GC(8) Concurrent Preclean 0.523ms
GC(8) Concurrent Abortable Preclean
GC(8) Concurrent Abortable Preclean 935.176ms
GC(8) Pause Remark 512M->512M(989M) 512.290ms
GC(8) Concurrent Sweep
GC(8) Concurrent Sweep 310.167ms
GC(8) Concurrent Reset
GC(8) Concurrent Reset 0.404ms
GC(9) Pause Young (Allocation Failure) 349M->349M(989M) 369.925ms

那如果禁用类卸载会发生什么呢?其实在禁用类卸载时,也会禁用常规 GC 周期中的字符串表清理,执行结果如下:

$ java -XX:+UseConcMarkSweepGC -Xmx2g -Xms2g -verbose:gc -XX:-ClassUnloading -XX:StringTableSize=6661443 InternMuchGC(11) Pause Young (Allocation Failure) 273M->308M(989M) 338.999ms
GC(12) Pause Initial Mark 314M->314M(989M) 66.586ms
GC(12) Concurrent Mark
GC(12) Concurrent Mark 175.625ms
GC(12) Concurrent Preclean
GC(12) Concurrent Preclean 0.539ms
GC(12) Concurrent Abortable Preclean
GC(12) Concurrent Abortable Preclean 2549.523ms
GC(12) Pause Remark 696M->696M(989M) 133.920ms
GC(12) Concurrent Sweep
GC(12) Concurrent Sweep 175.949ms
GC(12) Concurrent Reset
GC(12) Concurrent Reset 0.463ms
GC(14) Pause Full (Allocation Failure) 859M->0M(989M) 1541.465ms  <---- !!!
GC(13) Pause Young (Allocation Failure) 859M->0M(989M) 1541.515ms

有上述执行结果可知,发生Stop The World GC。而对于CMS而言,有 ExplicitGCInvokesConcurrentAndUnloadsClasses 可以缓解这种情况,假设用户有时会调用System.gc() 。

总结

String.intern()的使用需要综合考虑吞吐量、内存占用、暂停时间问题,这些问题会直接影响JVM运行的性能,其实手动重复数据删除器/interner可能更能帮助我们获得理想的效果。


文章转载自:

http://KmI8hvV3.ggmLs.cn
http://AnAwvyi4.ggmLs.cn
http://eBdaoNwC.ggmLs.cn
http://HJhgI7DW.ggmLs.cn
http://vd3Ot5Kx.ggmLs.cn
http://V3chCO8g.ggmLs.cn
http://6IGqVbGL.ggmLs.cn
http://ddKHRoWg.ggmLs.cn
http://7mpJkDw4.ggmLs.cn
http://C87IG34S.ggmLs.cn
http://hDMgw5wZ.ggmLs.cn
http://9tilwDfE.ggmLs.cn
http://pUvgl1uG.ggmLs.cn
http://nf9b69ce.ggmLs.cn
http://gCUvRZEH.ggmLs.cn
http://KpWVySVq.ggmLs.cn
http://AiUgjaaN.ggmLs.cn
http://nBO3ZMEN.ggmLs.cn
http://3ANCU9Nu.ggmLs.cn
http://YEzBHpAy.ggmLs.cn
http://wzkiBzjn.ggmLs.cn
http://hRmXwDfO.ggmLs.cn
http://XGKBRili.ggmLs.cn
http://s9XPxYCt.ggmLs.cn
http://Qx2L7Wi9.ggmLs.cn
http://2MfL58D8.ggmLs.cn
http://srWO0VYW.ggmLs.cn
http://IQtD0SkS.ggmLs.cn
http://wu0Hf8Zg.ggmLs.cn
http://gPxtQNSa.ggmLs.cn
http://www.dtcms.com/wzjs/728569.html

相关文章:

  • 车票在线制作网站wordpress系列文章
  • 怎么做找券网站网页设计页面配色分析
  • 加快网站平台建设app推广注册赚钱
  • 制作网站问题和解决方法万网域名注册商
  • 交换链接适用于哪些网站网站建设价格标准报价
  • 高埗东莞网站建设上海网站建设公司怎么样
  • 哪个网站可以建设网站《php网站开发》课程资料
  • 做网站翻页怎么做私募网站建设
  • 永定路网站建设伦教网站设计
  • 公司网站建设知识3d 网站设计
  • 网站开发学校计算机学院网站建设
  • 网站免费网站app专业版式设计网站
  • 上海通信管理局网站站长工具同大全站
  • 网站的建设域名空间建设包包网站的目的
  • wordpress做网站容易吗珠海网站建设公司有哪些
  • 怎么做qq代刷网站云指官网
  • 个人网站企业备案区别做网站找哪家又便宜又好
  • 海淀高端企业网站建设马蹄室内设计网论坛
  • 江西省赣州市官网上海seo优化服务公司
  • 寻找专业网站建设天津市住房城乡建设部网站
  • 自己开的网站 可以做代销吗怎么让WORDPRESS首页显示菜单
  • 网站开发技术岗位职责云主机可以用来做什么
  • 河南做网站高手排名女生学网络营销这个专业好吗
  • 申报湖南创新型省份建设专项网站网站开发德菁
  • 广东省建设安全卡查询网站网站模块 带采集
  • 衣服网站设计做搜狗pc网站优化
  • 单页网站作用是什么网上推广公司
  • 企业网站建设58同城创意网页设计题库
  • 建设网站要学什么福州医疗网站建设
  • 网站外包 多少钱深圳小程序开发设计