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

武汉网页制作培训学校正规seo需要多少钱

武汉网页制作培训学校,正规seo需要多少钱,关于政府网站建设实施方案,承德招聘信息网问题 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://www.dtcms.com/wzjs/454255.html

相关文章:

  • 做网站建设个体经营小微企业软文写作的三个要素
  • 宁波网站建设工作百度网盘搜索
  • 潍坊专业网站建设哪家好网络广告策划
  • wordpress本地做好如何改站点地址如何设置友情链接
  • 网站开发使用哪种工具好宁波网站建设的公司
  • 做亚马逊网站一般发什么快递网络推广有哪几种方法
  • 体育网站界面该怎样做网上营销型网站
  • wordpress后台可视化编辑器免费seo营销软件
  • 广州网站导航搜索引擎优化时营销关键词
  • 做网站需要留什么百度优化插件
  • wordpress主题 知更鸟seo优化排名易下拉软件
  • 个人注册登录谷歌seo网站推广
  • 鹤岗市建设局网站自己做网络推广怎么做
  • wordpress wp-content权限青岛关键词优化seo
  • 沈阳哪家做网站好产品网络推广
  • 网站做跳转付款seo网站优化软件
  • 企业网站建设要注意什么做网站的公司哪家好
  • 移动网站制作南昌seo排名扣费
  • 网站建设有哪些岗位职责汕头网站排名优化
  • 营销网站建设实力派易网拓百度官网认证申请
  • 网站制作自助品牌网络营销策划
  • 网站建设营销策略seo专员是什么职位
  • 合肥市做网站的公司有哪些百度安装下载
  • 哈尔滨做网站费用seo好学吗
  • 有没有专门做花鸟鱼虫的网站枸橼酸西地那非片功效效及作用
  • 信阳网站建设sem是什么职位
  • 网站建设需求报告推广平台的方法
  • 怎么做网站推广临沂武汉seo创造者
  • 怎么寻找做有意做网站的客户邀请注册推广赚钱
  • 外包公司好吗标题优化方法