java如果把字符串转成对象_Java中的重复对象:不仅仅是字符串

fe8510e68de614ef4386be4737f0d3c6.png

当Java应用程序消耗大量内存时,它本身就会出现问题,并可能导致GC压力增加和GC暂停时间过长。在我之前的一篇文章中,我讨论了Java中常见的内存浪费源:重复字符串。两个 java.lang.String 对象, ab 在重复时 a != b && a.equals(b)。换句话说,在JVM存储器中有两个(或更多)单独的字符串具有相同的内容。此问题经常发生,尤其是在业务应用程序中。在这样的应用程序中,字符串代表了许多真实世界的数据,然而,相应的数据域(例如客户名称,国家名称,产品名称)是有限的并且通常很小。根据我的经验,在未经优化的Java应用程序中,重复的字符串通常会浪费5到30%的堆。但是,你有没有想过其他类的实例,包括数组,有时也会重复,浪费了相当多的内存?如果没有,请继续阅读。

对象复制方案

只要某种类型的不同对象的数量有限,但应用程序不断创建此类对象而不尝试缓存/重用现有对象,就会发生内存中的对象复制。以下是我看到的对象复制的几个具体示例:

  • 在Hadoop文件系统(HDFS)NameServer中, byte[] 数组而不是 Strings用于存储文件名。当在不同目录中存在具有相同名称的文件时,相应的 byte[] 阵列是重复的。有关 详细信息,请参阅此票证。
  • 在一些监视系统中,从被监视实体(机器,应用程序,组件等)接收的周期性“事件”或“更新”被表示为具有两个主要字段的小对象:时间戳和值。当许多更新同时到达并且所有更新都具有相同的值(例如,0或1表示被监视实体的健康状况良好)时,会创建许多重复对象。
  • 该 蜂房数据仓库曾经有过以下问题。当针对具有大量分区的同一DB表执行多个并发查询时,每个分区的元数据的单独的每个查询副本被加载到内存中。分区元数据表示为 java.util.Properties 实例。因此,针对2000个分区运行50个并发查询,Properties 用于为这些分区中的每个分区创建50个相同的副本 ,或者总共100,000个这样的集合,这消耗了大量内存。有关详细信息,请参阅此票证。

这只是几个例子。其他不太明显的包括存储相同消息的多个相同字节缓冲区,具有表示某些频繁出现的数据组合的相同内容的多个(通常是小的)对象集,等等。

摆脱重复的对象

如上所述,字符串是一类特别容易重复的对象。很久以前JDK开发人员已经实现了这个问题,并使用String.intern()方法解决了这个问题。上面提到的文章详细讨论了它。简而言之,此方法使用具有有效弱引用的全局字符串缓存(池)。如果它还没有在缓存中,它会保存并返回给定的字符串实例,或者返回具有相同值的缓存字符串实例。String.intern() 曾经不是很好的 性能和可扩展性 从JDK 7开始大幅改进。因此,当没有过度使用时,它可能是许多应用程序的良好解决方案。然而,在讨论这篇文章在高度并发或性能关键的应用程序中,它可能成为瓶颈,可能需要一种不同的“手动”实习方法。

让我们考虑其他对象实习。请记住,以下讨论仅适用于不可变对象,即在创建后不会更改的对象。如果对象内容可以改变,消除重复变得更加困难,并且需要定制的逐案解决方案。

最广泛使用的现成实习功能由Guava库通过com.google.commmon.collect.Interners类提供。此类有两个返回内部实例的关键方法: newStrongInterner()newWeakInterner()。弱内部函数最终会释放不再需要的对象(未在任何地方强烈引用),通常使用较少的内存,因此更频繁地使用。它被实现为具有类似于标准JDK的弱键的并发哈希集 ConcurrentHashMap。在许多情况下,这是一个很好的选择,有助于通过较小的CPU性能开销大幅减少内存占用,这通常会减少GC时间。但是,请考虑以下情况:

  • 一些C类有2000万个实例,每个实例占32个字节
  • 其中1000万个实例彼此完全相同,另外1000万个实例都是截然不同的。在实践中,这种尖锐的划分几乎从未发生过,但是,大约一半的物体仅表示少数独特的值,而在另一半中,大多数物体很少或没有重复,这种情况非常常见。简化的划分使我们的计算更容易。

在这种情况下,当我们不实习任何C实例时,它们使用32 * 20M = 640M字节。但是如果我们intern() 为每个人调用番石榴会发生什么 呢?

前1000万个对象将成功减少到只有一个C实例,占用的内存可以忽略不计。但是,对于剩下的1000万个对象中的每一个,都没有节省,因为它们中的每一个都是唯一的。尽管如此,Guava弱内部人员将维持一个内部表格,其中包含1000万个条目以容纳这些对象中的每一个。该表将使用com.google.common.collect.MapMakerInternalMap$WeakKeyDummyValueEntry 每个实习对象的一个类实例 ,以及引用每个条目的内部数组中的一个插槽。a的大小 WeakKeyDummyValueEntry是40个字节,因此每个实习对象需要40 + 4 = 44个字节。44 * 10M = 440M。添加到32 * 10M = 320M,C的唯一实例仍然占用,现在总内存占用量为760M字节!换句话说,我们使用更多的内存比以前,而不是更少。

这种情况可能有点极端,但在实践中,一般规则仍然存在:如果在给定的一组对象中,唯一对象的百分比很高,那么传统的内部存储器可以节省内存,存储对每个对象的引用给予它,可能很小,如果不是负面的话。我们可以做得更好吗?

固定大小的阵列,无锁(FALF)内部

事实证明,如果我们不需要每个唯一对象的单个副本,而只是想节省内存,并且可能仍然有一些重复的对象 - 换句话说,如果我们同意“机会性地”重复删除对象 - 有一个简单而有效的解决方案。我没有在文献中看到它,所以我把它命名为“固定大小数组,无锁(FALF)Interner”。

此interner实现为一个小的,固定大小,基于开放哈希映射的对象缓存。当存在高速缓存未命中时,给定插槽中的高速缓存对象始终用新对象替换。没有锁定和没有同步,因此没有相关的开销。基本上,这个缓存是基于这样的想法:具有值X的具有许多副本的对象具有更高的机会保持在缓存中足够长的时间以保证在错过驱逐X之前自己的几个缓存命中并且用具有对象的对象替换它。一个不同的值Y.这是这个interner的一个可能的实现:

/** Fixed size array, lock free object interner */staticclassFALFInterner<T>{staticfinalintMAXIMUM_CAPACITY=1<<30;privateObject[]cache;FALFInterner(intexpectedCapacity) {cache=newObject[tableSizeFor(expectedCapacity)];}Tintern(Tobj) {intslot=hash(obj)&(cache.length-1);TcachedObj=(T)cache[slot];if(cachedObj!=null&&cachedObj.equals(obj))returncachedObj;else{cache[slot]=obj;returnobj;}}/** Copied from java.util.HashMap */staticinthash(Objectkey) {inth;return(key==null)?0: (h=key.hashCode())^(h>>>16);}/*** Returns a power of two size for the given target capacity.* Copied from java.util.HashMap.*/staticinttableSizeFor(intcap) {intn=cap-1;n|=n>>>1;n|=n>>>2;n|=n>>>4;n|=n>>>8;n|=n>>>16;return(n<0)?1: (n>=MAXIMUM_CAPACITY)?MAXIMUM_CAPACITY:n+1;}}

为了比较FALF interner和Guava weak interner的性能,我写了一个简单的多线程基准测试。代码生成并实现从遵循高斯分布的随机数派生的字符串。也就是说,具有某些值的字符串比其他字符串更频繁地生成,因此将导致更多重复。在这个基准测试中,FALF interner运行速度比Guava interner快约17%。但是,并非全部。当我测量内存占用时 jmap -histo:live 基准测试完成后运行,但在退出之前,事实证明,使用FALF interner,使用的堆大小比Guava interner小近30倍!这是固定大小的小缓存与弱散列映射的内存占用量的差异,其中每个独特对象都有一个条目。

公平地说,FALF interner通常需要比传统的,一刀切的内部调整器更多的调整。首先,由于缓存大小是固定的,您需要仔细选择它。一方面,为了最大限度地减少未命中,这个大小应该足够大 - 理想情况下等于你实习生类型的唯一对象的数量。另一方面,我们的目标是最小化已用内存,因此在实践中,您可能会选择(更大)更小的大小,该大小大致等于具有大量重复项的对象的数量。

另一个重要的考虑因素是为被拦截对象选择散列函数。在一个小的,固定大小的缓存中,尽可能均匀地在插槽中分布对象非常重要,以避免不经常使用许多插槽时的情况,而有一个小组,其中每个插槽由几个对象值争用很多副本。这种争用将导致缓存遗漏许多重复,因此,更大的内存占用。当这种情况发生在具有非常简单hashCode() 方法的类的实例时 (例如,代码类似于java.lang.String 类中的代码 ),它可能表明该散列函数实现是不合适的。更高级的哈希函数,就像com.google.common.hash.Hashing提供的哈希函数之一 类,可以大大提高FALF interner的效率。

检测重复对象

到目前为止,我们还没有讨论过开发人员如何确定应用程序中的哪些对象有很多重复项,因此需要进行实习。对于大型应用程序,这可能是非平凡的。即使您可以猜测哪些对象可能重复,也很难估计其确切的内存影响。根据经验,解决此问题的最佳方法是生成应用程序的堆转储,然后使用工具对其进行分析。

堆转储本质上是正在运行的JVM堆的完整快照。它可以通过调用jmap 实用程序在任意时刻进行 ,也可以将JVM配置为在失败时自动生成它 OutOfMemoryError。如果你谷歌“JVM堆转储”,你会立即看到一堆关于这个主题的相关文章。

堆转储是一个大小与JVM堆大小相同的二进制文件,因此只能使用特殊工具读取和分析它。有许多这样的工具,包括开源和商业。最流行的开源工具是Eclipse MAT; 还有VisualVM和一些不那么强大,鲜为人知的工具。商业工具包括通用Java分析器:JProfiler和YourKit,以及JXRay - 专门为堆转储分析构建的工具。

与大多数其他工具不同,JXRay会立即分析堆转储以解决大量常见问题,包括重复字符串和其他对象。目前,对象比较浅薄。也就是说,只有两个对象(例如 ArrayLists)x0, x1, x2, ... 以相同的顺序引用完全相同的对象组时才被认为是重复的 。换一种说法,两个对象 ab 被认为是平等的,都指向其他对象 ab 使用位是相等的,有点。

JXRay运行一次给定的堆转储,并生成一个包含HTML格式的所有收集信息的报告。这种方法的优点是,您可以随时随地查看分析结果,并轻松与他人分享。这也意味着您可以在任何机器上运行该工具,包括数据中心中功能强大但功能强大的“无头”机器。

从JXRay获得报告后,在您喜欢的浏览器中打开它并展开相关部分。你可能会看到这样的事情:

e82f730581621bf196ccdf5caaa8c42d.png

因此,在此转储中,24.7%的已使用堆被重复的非数组非集合对象浪费!上表列出了其实例对此开销贡献最大的所有类。要查看这些对象的来源(哪些对象引用它们,一直到GC根),向下滚动到报告的“昂贵数据字段”或“完整参考链”子部分,展开它,然后单击相关表中的一行。以下是上述类之一的示例 TopicPartition

5e28f6401734e7077b9ebf6bded7828a.png

从这里,我们可以很好地了解哪些数据结构可以管理有问题的对象。

总而言之,重复对象,即具有相同内容的同一类的多个实例,可能成为Java应用程序的负担。它们可能会浪费大量内存和/或增加GC压力。衡量此类对象影响的最佳方法是获取堆转储并使用JXRay之类的工具对其进行分析。当重复对象是不可变的时,您可以使用现成的或自定义的内部实现来减少此类对象的数量,从而减少其内存影响。可变复制对象更难以摆脱,并且可能只能通过逐个定制的解决方案来消除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

批处理 设置电脑最佳性能_批处理最佳做法

批处理 设置电脑最佳性能大多数应用程序至少具有一个批处理任务&#xff0c;在后台执行特定的逻辑。 编写批处理作业并不复杂&#xff0c;但是您需要了解一些基本规则&#xff0c;我将列举一些我发现最重要的规则。 从输入类型的角度来看&#xff0c;处理项目可以通过轮询处理…

JVM体系结构:JVM类加载器和运行时数据区

各位读者好&#xff01; 在JVM系列的上一篇文章中&#xff0c;开发人员了解了Java虚拟机&#xff08;JVM&#xff09;及其体系结构。 本教程将帮助开发人员正确回答以下主题的问题&#xff1a; ClassLoader子系统 运行时数据区 1.简介 在继续之前&#xff0c;让我们看一下Ja…

mysql5.6特性_MySQL5.6新版本特性

MySQL已发布新的系列版本5.6.x&#xff0c;如果打算升级的朋友可以尝试&#xff0c;虽然目前没有收到新版本的使用反馈&#xff0c;但凭借MySQL占据市场份额来看&#xff0c;新版本的确值得期待。五大特性&#xff1a;优化器的改进MySQL Optimizer 团队做了大量的工作为了不断的…

java计算整数出现的次数_[剑指offer题解][Java]1到n整数中1出现的次数

前言众所周知&#xff0c;《剑指offer》是一本“好书”。如果你是个算法菜鸡&#xff08;和我一样&#xff09;&#xff0c;那么最推荐的是先把剑指offer的题目搞明白。对于剑指offer题解这个系列&#xff0c;我的写作思路是&#xff0c;对于看过文章的读者&#xff0c;能够做到…

mysql 更改root密码及 主机_设置更改root密码(远程,本地)、连接mysql、mysql常用命令...

设置更改root密码1、将mysql加入环境变量中[rootcentos7 ~]# grep mysql /etc/profileexport PATH/usr/local/mysql/bin/:$PATH2、直接登录&#xff0c;无密码[rootcentos7 ~]# mysql -uroot3、方式一&#xff1a;设置密码[rootcentos7 ~]# mysqladmin -uroot password 123456W…

从NetBeans运行和调试WildFly Swarm应用程序

使用NetBeans的Java EE开发人员习惯于直接在NetBeans所选择的应用程序服务器中运行和调试其瘦战应用程序。 在开发打包为ber或镂空jars的微服务时&#xff0c;您期望使用相同的轻松方式进行运行和调试。 好消息是您可以。 在本文中&#xff0c;我将逐步演示如何在NetBeans中运行…

hazelcast入门教程_Hazelcast入门

hazelcast入门教程7月&#xff0c;我写了一个博客向Java开发人员介绍erlang&#xff0c;重点介绍了这两种语言之间的一些异同。 erlang虚拟机具有许多令人印象深刻的内置功能&#xff0c;其中之一是它们独立于位置且可以相互通信。 这意味着可以通过编写很少的代码行在VM之间同…

[MEGA DEAL]完整的Java编程训练营(94%折扣)

成为Java Master的10门课程&#xff08;83.5小时&#xff09;&#xff1a;使用JavaFX的设计UI&#xff0c;利用设计模式&#xff0c;Master Multithreading等 嘿&#xff0c;怪胎&#xff0c; 本周&#xff0c;在我们的JCG Deals商店中 &#xff0c;我们提供了另一个超值优惠…

java重排序_Java内存模型FAQ(四)重排序意味着什么?

译者&#xff1a;Alex在很多情况下&#xff0c;访问一个程序变量(对象实例字段&#xff0c;类静态字段和数组元素)可能会使用不同的顺序执行&#xff0c;而不是程序语义所指定的顺序执行。编译器能够自由的以优化的名义去改变指令顺序。在特定的环境下&#xff0c;处理器可能会…

JVM体系结构101:了解您的虚拟机

Java虚拟机&#xff08;JVM&#xff09;架构和Java字节码101的初学者速成班 Java应用程序无处不在&#xff0c;它们在我们的手机&#xff0c;平板电脑和计算机上。 在许多编程语言中&#xff0c;这意味着要多次编译代码才能使其在不同的OS上运行。 对于作为开发人员的我们来说…

flask对mysql数据库增删改查_Flask学习(三) - Flask-SQLAlchemy对数据库增删改查

Flask-SQLAlchemy对数据库增删改查安装pip install flask-sqlalchemy具体不多说了&#xff0c;主要是对数据库进行简单的增删改查&#xff0c;上代码&#xff0c;看注释app.route(/)def index():#增加article1 Article(titletest1, contentthe first test)db.session.add(arti…

带有Jersey的JAX-RS教程,用于RESTful Web服务

在当今世界&#xff0c;数据扮演着非常重要的角色。 如此众多的应用程序将各种类型的数据用于不同的操作&#xff0c;所以最重要的方面是应用程序之间的通信。 当应用程序可以通信时&#xff0c;它们之间的数据共享变得容易。 就像在亚洲运行的应用程序向在欧洲运行的应用程序…

java swing 打开文件_java swing实现打开Excel文件并进行处理

这里选择Excel文件是业务需要&#xff0c;话不多说1、引入对Excel处理的相关依赖org.apache.poipoi3.16org.apache.poipoi-ooxml3.162、进行swing的相关布局代码&#xff1a;package com.mozarta;import org.apache.poi.ss.usermodel.Row;import org.apache.poi.xssf.usermodel…

java的对象对象映射_Java对象到对象映射器

java的对象对象映射我在该项目上使用了Dozer一段时间。 但是&#xff0c;最近我遇到了一个非常有趣的错误&#xff0c;这促使我环顾四周&#xff0c;并尝试使用其他“对象到对象”映射器。 这是我找到的工具列表&#xff1a; 推土机&#xff1a;推土机是Java Bean到Java Bea…

java parallelstream_关于Java8 parallelStream并发安全的深入讲解

背景Java8的stream接口极大地减少了for循环写法的复杂性&#xff0c;stream提供了map/reduce/collect等一系列聚合接口&#xff0c;还支持并发操作&#xff1a;parallelStream。在爬虫开发过程中&#xff0c;经常会遇到遍历一个很大的集合做重复的操作&#xff0c;这时候如果使…

使用Google Cloud Storage托管您的Maven工件

如果您使用Google Cloud并将Java用于项目&#xff0c;那么Google Cloud Storage是托管团队工件的理想场所。 它很容易设置&#xff0c;而且很便宜。 如果您对它们的功能不特别感兴趣&#xff0c;那么它比设置现有存储库选项&#xff08;jfrog&#xff0c;nexus&#xff0c;arc…

java+map对象判断空值_java判断map中是否存在指定对象

Map判断是否包含指定的value使用containsValue方法。(推荐&#xff1a;java视频教程)定义containsValue(Object value) 如果此映射将一个或多个键映射到指定值&#xff0c;则返回 true示例&#xff1a;/**** Map集合判断是否包含value**/public class MapDemo{public static vo…

excel查重复_毕业季 | 如何降低论文的查重率

毕业季吾日三省吾身实验做完了吗&#xff1f;论文写完了吗&#xff1f;查重能通过吗&#xff1f;学术圈的前辈告诉我们&#xff0c;只有站在巨人的肩膀上才能看得更远。在撰写一篇论文时&#xff0c;为保证质量和可靠性&#xff0c;难免需要引用前人的成果&#xff0c;这也反映…

Java,JavaFX的流畅设计风格进度栏

按照承诺&#xff0c;刚刚发布的Java JavaFX主题JMetro 4.6版为进度栏带来了新样式。 进度栏有两种可能的状态&#xff1a;确定和不确定&#xff0c;新的JMetro版本具有这两种状态。 在本文中&#xff0c;我还将详细介绍一些我在JMetro中遵守的API设计原则。 JMetro API设计原…

安卓最新系统_成纺移动校园(移动办公系统)V3.2.1 安卓最新版

成纺移动校园(移动办公系统)是额一个非常实用的办公工具。您可以使用该软件及时浏览最新的校园信息&#xff0c;同时涵盖许多功能&#xff0c;例如时间表查询&#xff0c;会议安排&#xff0c;校园地图&#xff0c;校车等。有需要的用户欢迎来绿色先锋网下载。 成纺移动校园简介…