使用这些先进的GC技术提高应用程序性能

应用程序性能是我们的首要考虑因素,垃圾收集优化是取得小而有意义的进步的好地方
垃圾收集优化
自动化垃圾收集(与JIT HotSpot编译器一起)是JVM中最先进,最有价值的组件之一,但是许多开发人员和工程师对垃圾收集(GC),其工作方式以及如何影响应用程序性能的了解都很少。 。

首先,GC还可以做什么? 垃圾收集是堆中对象的内存管理过程。 将对象分配给堆时,它们会经历几个收集阶段–通常相当快,因为​​堆中的大多数对象的寿命很短。

垃圾收集事件包含三个阶段-标记,删除和复制/压缩。 在第一阶段,GC遍历堆,并将所有内容标记为活动(引用)对象,未引用对象或可用内存空间。 然后删除未引用的对象,并压缩剩余的对象。 在分代的垃圾收集中,对象“老化”并通过其生活中的3个空间进行提升-伊甸园,幸存者空间和保有权(旧)空间。 这种移动也作为压实阶段的一部分发生。

但是足够了,让我们进入有趣的部分!

了解Java中的垃圾回收(GC)

自动化GC的一大优点是,开发人员实际上不需要了解其工作原理。 不幸的是,这意味着许多开发人员不了解其工作原理。 了解垃圾收集和许多可用的GC,有点像了解Linux CLI命令。 从技术上讲,您不需要使用它们,但是了解和使用它们会对您的生产率产生重大影响。

就像CLI命令一样,这里有绝对的基础知识。 ls命令查看父文件夹中的文件夹列表, mv将文件从一个位置移动到另一个位置,等等。在GC中,这些类型的命令等同于知道有多个GC可供选择,并且GC可能引起性能问题。 当然,还有很多东西要学习(关于使用Linux CLI和垃圾回收)。

学习Java的垃圾回收过程的目的不仅是免费(无聊)的对话入门者,其目的还在于学习如何针对特定环境有效地实现和维护具有最佳性能的正确GC。 知道垃圾回收会影响应用程序性能是基础,并且有许多先进的技术可以增强GC性能并减少其对应用程序可靠性的影响。

GC性能问题

1.内存泄漏–

通过了解堆结构以及如何执行垃圾回收,我们知道内存使用量逐渐增加,直到发生垃圾回收事件并且使用率回落为止。 被引用对象的堆利用率通常保持稳定,因此下降幅度应大致相同。

发生内存泄漏时,每个GC事件都会清除一小部分堆对象(尽管没有使用许多留在后面的对象),因此堆利用率将继续增加,直到堆内存已满,并且将抛出OutOfMemoryError异常。 原因是GC仅将未引用的对象标记为删除。 因此,即使不再使用引用的对象,也不会从堆中清除该对象。 有一些有用的编码技巧可以防止这种情况,稍后我们将介绍。

2.连续的“停止世界”活动–

在某些情况下,垃圾回收可以称为Stop the World事件,因为当垃圾回收发生时,JVM中的所有线程(并因此在其上运行的应用程序)都将停止以允许GC执行。 在健康的应用程序中,GC执行时间相对较短,并且不会对应用程序性能产生太大影响。

但是,在次优情况下,“停止世界”事件可能会极大影响应用程序的性能和可靠性。 如果GC事件需要“停止世界”暂停并需要2秒钟的时间执行,则该应用程序的最终用户将遇到2秒的延迟,因为运行该应用程序的线程被停止以允许GC。

当发生内存泄漏时,连续的“停止世界”事件也是有问题的。 由于每次执行GC都会清除较少的堆内存空间,因此剩余内存填满的时间会更少。 当内存已满时,JVM会触发另一个GC事件。 最终,JVM将运行重复的Stop the World事件,从而引起严重的性能问题。

3. CPU使用率–

而这全都取决于CPU使用率。 连续GC /“停止世界”事件的主要症状是CPU使用率激增。 GC是一项计算量大的操作,因此所花费的资源不应该超过它的CPU能力。 对于运行并发线程的GC,CPU使用率可能更高。 为您的应用程序选择合适的GC将对CPU使用率产生最大的影响,但是还有其他方法可以优化以提高该领域的性能。

从围绕垃圾收集的性能问题中我们可以了解到,尽管高级GC获得了(并且它们已经相当先进),但其致命弱点仍然保持不变。 冗余且不可预测的对象分配。 要提高应用程序性能,仅选择正确的GC是不够的。 我们需要了解流程的工作方式,并且需要优化代码,以使我们的GC不会占用过多的资源或在应用程序中造成过多的暂停。

世代GC

在深入研究不同的Java GC及其对性能的影响之前,了解世代垃圾收集的基础很重要。 世代GC的基本概念基于这样的思想,即对堆中某个对象的引用存在的时间越长,将其标记为删除的可能性就越小。 通过用具有象征意义的“年龄”标记对象,可以将它们分隔到不同的存储空间,以使GC进行标记的频率降低。

将对象分配给堆时,会将其放置在所谓的Eden空间中。 那是对象开始的地方,在大多数情况下,它们是标记为删除的地方。 在该阶段幸存的对象将“庆祝生日”,并复制到Survivor空间。 此过程如下所示:

伊甸园和幸存者空间组成了年轻一代。 这是大部分操作发生的地方。 当(如果)年轻一代中的某个对象达到一定年龄时,该对象将被提升到终身(也称为旧)空间。 根据年龄划分对象内存的好处是GC可以在不同级别上运行。

次要GC是仅关注年轻一代的集合,实际上完全忽略了Tenured空间。 通常,年轻代中的大多数对象都标记为删除,并且不需要主要或完整GC(包括旧代)来释放堆上的内存。 当然,必要时会触发“主要”或“完全” GC。

在此基础上优化GC操作的一个快速技巧是调整堆区域的大小,以最适合您的应用程序的需求。

收集器类型

有许多可用的GC可供选择,尽管G1成为Java 9中的默认GC ,但它最初旨在替代低暂停的CMS收集器,因此使用吞吐量收集器运行的应用程序可能更适合保留其当前收集器。 对于Java垃圾收集器,了解操作差异以及性能影响差异仍然很重要。

吞吐量收集器

更适合需要针对高吞吐量进行优化的应用程序,并且可以交易更高的延迟来实现。

序列号–

串行收集器是最简单的一种,也是您最不可能使用的一种,因为它主要用于单线程环境(例如32位或Windows)和小型堆。 该收集器可以垂直扩展JVM中的内存使用量,但需要几个主要/完全GC才能释放未使用的堆资源。 这会导致频繁的Stop the World暂停,从而使它在所有意图和目的上都无法在面向用户的环境中使用。

平行 -

顾名思义,此GC使用并行运行的多个线程来扫描并压缩堆。 尽管Parallel GC使用多个线程进行垃圾回收,但它在运行时仍会暂停所有应用程序线程。 Parallel收集器最适合需要针对最佳吞吐量进行优化并且可以忍受更高延迟的应用程序。

低暂停时间收集器

大多数面向用户的应用程序都需要低暂停GC,因此长时间或频繁的暂停不会影响用户体验。 这些GC都是为了优化响应能力(时间/事件)和强大的短期性能。

并发标记扫描(CMS)–

与并行收集器相似,并发标记扫描(CMS)收集器利用多个线程来标记和清除(删除)未引用的对象。 但是,此GC仅在以下两个特定实例中启动“停止世界”事件:

(1)在初始化根的初始标记(旧代对象中可以从线程入口点或静态变量访问的对象)或main()方法的任何引用时,等等

(2)当应用程序在算法同时运行时更改了堆的状态时,迫使它返回并进行最后的修改以确保它标记了正确的对象

G1 –

垃圾第一收集器(通常称为G1)利用多个后台线程扫描通过堆将其划分为多个区域。 它的工作方式是先扫描那些包含最多垃圾对象的区域,然后为其命名(垃圾优先)。

这种策略减少了在后台线程完成对未使用对象的扫描之前耗尽堆的机会,在这种情况下,收集器将不得不停止应用程序。 G1收集器的另一个优点是它可以在移动过程中压缩堆,这是CMS收集器仅在完全Stop the World收集期间执行的操作。

改善GC性能

垃圾收集的频率和持续时间直接影响应用程序的性能,这意味着可以通过减少这些指标来优化GC流程。 有两种主要方法可以做到这一点。 首先,通过调整年轻人和老年人的堆大小 ,其次,以减少对象分配和提升的速度

在调整堆大小方面,它并不像人们期望的那么简单。 合理的结论是,增加堆大小将减少GC频率,同时增加持续时间,而减少堆大小将减少GC持续时间,同时增加频率。

不过,事实是,次要GC的持续时间不取决于堆的大小,而是取决于可以幸存的对象数量。 这意味着对于大多数创建寿命短的对象的应用程序,增加年轻代的大小实际上可以减少GC的持续时间频率。 但是,如果增加年轻代的大小会导致需要在幸存者空间中复制的对象显着增加,则GC暂停将花费更长的时间,从而导致延迟增加。

编写GC高效代码的3个技巧

提示1:预测收集容量–

所有标准Java集合以及大多数自定义和扩展的实现(例如Trove和Google的Guava)都使用基础数组(基于原始或对象的数组)。 由于数组一旦分配就不会改变大小,因此在许多情况下向集合中添加项目可能会导致丢弃旧的基础数组,而使用较大的新分配的数组。

即使未提供预期的集合大小,大多数集合实现都尝试优化此重新分配过程并将其保持在摊销后的最小值。 但是,通过在构造时为集合提供预期的大小可以达到最佳效果。

提示2:直接处理流–

例如,在处理数据流(例如从文件读取的数据或通过网络下载的数据)时,通常会看到以下内容:

byte[] fileData = readFileToByteArray(new File("myfile.txt"));

然后,可以将结果字节数组解析为XML文档,JSON对象或协议缓冲区消息,以列举一些常用的选项。

当处理大文件或大小无法预测的文件时,这显然不是一个好主意,因为在JVM无法实际分配整个文件大小的缓冲区的情况下,这会使我们面临OutOfMemoryErrors。

解决此问题的更好方法是使用适当的InputStream(在这种情况下为FileInputStream),将其直接输入解析器,而无需先将整个内容读取到字节数组中。 所有主要库都公开了API以直接解析流,例如:

FileInputStream fis = new FileInputStream(fileName);
MyProtoBufMessage msg = MyProtoBufMessage.parseFrom(fis);

提示3:使用不可变对象–

不变性有很多优点。 很少受到关注的一个问题是它对垃圾收集的影响。

不变对象是指在构造对象之后其字段(在我们的情况下尤其是非原始字段)无法修改的对象。

不变性意味着不变容器引用的所有对象都是在容器构造完成之前创建的。 用GC的术语来说:容器至少与所保存的最小引用一样年轻。 这意味着,在年轻一代执行垃圾回收周期时,GC可以跳过位于老一代中的不可变对象,因为它可以确定它们不能引用正在收集的一代中的任何对象。

要扫描的对象越少,意味着要扫描的内存页面越少,而要扫描的内存页面就越少,意味着GC周期越短,这意味着GC暂停时间越短,总体吞吐量就越高。

有关更多技巧和详细示例,请查看这篇文章,其中涵盖了用于编写内存效率更高的代码的深入策略。

***非常感谢OverOps研发团队的Amit Hurvitz对本文的热情和见解!

翻译自: https://www.javacodegeeks.com/2018/08/improve-application-performance-gc.html

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

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

相关文章

mysql中lead_SqlServer2012中LEAD函数简单分析_MySQL

LEAD函数简单点说,就是把下一行的某列数据提取到当前行来显示,看示例更能解释清楚,先看测试用脚本DECLARE TestData TABLE(ID INT IDENTITY(1,1),Department VARCHAR(20),LastName VARCHAR(20),Rate FLOAT)INSERT INTO TestData(Department,L…

堆栈溢出回答了我们不知道的Java首要问题

您不应该错过的堆栈溢出问题集合: 这不是秘密; 我们都使用堆栈溢出。 它掌握了生命,宇宙以及几乎所有与代码相关的内容的答案。 该平台为开发人员,工程师和其他人员提供了一个找到他们所面临问题的答案的地方,或者至少…

sequelize连接mysql_node.js通过Sequelize 连接MySQL

node.js通过Sequelize 连接MySQL一.通过koa2脚手架构建项目1.1 安装koa-generator在终端输入:$ npm install -g koa-generator11.2 使用koa-generator生成koa2项目$ koa2 HelloKoa21成功创建项目后,进入项目目录,并执行npm install命令$ cd H…

杀java_java怎么杀掉java进程

展开全部java中有调用外部程序的e69da5e887aa62616964757a686964616f31333337623431类,Runtime.getRuntime().exec("./**.exe");此函数返回的是一个Process 对象,该对象在创建后,可以对它进行 destroy () 杀掉子进程、 waitFor () …

高级java开发_我最喜欢的Java高级开发人员书籍

高级java开发我上一篇博客文章 (我对高级Java开发人员的十个最喜欢的在线资源)的想法,是由Xiaoran Wang发表的“面向高级Java开发人员的十大网站”的启发。 Wang还写了一篇名为“面向Java高级开发人员的十大书籍”的文章。 就像关于高级Java开…

camera.swf java_java调用摄像头保存图片上传功能

在项目中要用到jsp页面实现网页采集摄像头图像,并实现上传的功能,页面主要js调用的,所以可以使用多种语言php,asp等都可以使用,而且是跨浏览器的。可以整合的到SSH项目中和struts1或者struts2项目中使用方法1:下载 pri…

java 北理工 教材_北理工《Java技术与应用》在线作业

北理工《Java技术与应用》在线作业可以代做所有奥鹏平台的作业、小论文、毕业论文、离线作业、考核作业、在线作业、在线测试,有需要的请联系本人一、单选题(共 20 道试题,共 40 分。)V1. Socket对象中____函数获取远程端口。A. getPort( )B. getLocalPo…

Apache Spark Job的剖析

Apache Spark是通用的大规模数据处理框架。 了解spark如何执行作业对于获取大部分作业非常重要。 关于Spark评估范式的简短回顾:Spark使用的是惰性评估范式,在该范式中,Spark应用程序在驱动程序调用“ Action”之前不会执行任何操作。 惰性…

scala本地调试_如何编写自己的Java / Scala调试器

scala本地调试在本文中,我们将探讨Java / Scala调试器的编写和工作方式。 诸如Windows的WinDbg或Linux / Unix的gdb之类的本机调试器通过操作系统直接提供给它们的钩子来获取其功能,以监视和操纵外部进程的状态。 JVM充当OS之上的抽象层,它提…

java content()_Java contentEquals() 方法

全屏Java contentEquals() 方法contentEquals() 方法用于将将此字符串与指定的 StringBuffer 比较。语法public boolean contentEquals(StringBuffer sb)参数sb -- 要与字符串比较的 StringBuffer。返回值如字符串与指定 StringBuffer 表示相同的字符序列,则返回 tr…

通过这5个简单的技巧减少GC开销

编写代码的五种简单方法,可提高内存效率,而无需花费更多时间或降低代码可读性 垃圾回收会为您的应用程序增加多少开销? 您可能不知道确切的数字,但您确实知道总有改进的余地。 尽管自动GC是最有效的过程,但是如果它过…

内部简单二进制编码(SBE)

SBE是用于金融行业的非常快速的序列化库,在本博客中,我将介绍一些使其快速发展的设计选择。 序列化的全部目的是对消息进行编码和解码,并且有很多可用的选项,从XML,JSON,Protobufer,Thrift&…

mingw64 下 java_在 Windows 10 64 位下安装 Mingw-w64

1、MinGW 的全称是:Minimalist GNU on Windows 。打开网址:http://www.mingw-w64.org/doku.php/download ,选择 MingW-W64-builds。如图1图12、下载包名:mingw-w64-install.exe。安装时报错:Cannot download repositor…

java实现layui分页_layui如何实现数据分页功能

我们先来看下官网的演示画面。具体代码:页面引入layui.css、 layui.js前台jsvar limitcount 10;var curnum 1;//列表查询方法function productsearch(productGroupId,start,limitsize) {layui.use([table,laypage,laydate], function(){var table layui.table,la…

java 合并到一行_mysql中将多行数据合并成一行数据

一个字段可能对应多条数据,用mysql实现将多行数据合并成一行数据例如:一个活动id(activeId)对应多个模块名(modelName),按照一般的sql语句:1 SELECT am.activeId,m.modelName2 FROM activemodel am3 JOIN model m4 ON am.modelId m.modelId5…

容器化Spring Data Cassandra应用程序

我正在继续学习Docker的旅程。 在这一点上,我仍然保持简单。 这次,我将解决将Spring和Cassandra应用程序转换为使用容器而不是在主机上本地运行的问题。 更准确地说,使用Spring Data Cassandra整理应用程序。 我希望我前几天看过进行此更改。…

使用React和Spring Boot构建一个简单的CRUD应用

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。 React的设计使创建交互式UI变得轻松自如。 它的状态管理非常有效,并且仅在…

使用CUBA进行开发–是Spring的重大转变吗?

阅读另一个供内部公司使用的Web项目的要求时,您(至少是我自己)通常会看到一个很普通的集合:定义明确的数据存储结构(或有时是现有的旧式DB),大量的数据输入形式,非常复杂的业务逻辑&…

java 伴随矩阵_C#计算矩阵的逆矩阵方法实例分析

本文实例讲述了C#计算矩阵的逆矩阵方法。分享给大家供大家参考。具体如下:1.代码思路1)对矩阵进行合法性检查:矩阵必须为方阵2)计算矩阵行列式的值(Determinant函数)3)只有满秩矩阵才有逆矩阵,因此如果行列式的值为0(在代码中以绝对值小于1E-…

java 代码造假_老板居然让我在Java项目中“造假”

1. 前言老板说,明天甲方要来看产品,你得造点数据,而且数据必须是“真”的,演示效果要好看一些,这样他才会买我们的产品,我好明年给你换个嫂子。一般开发接到这种过分要求都不会很乐意去做,这完全…