java timer开销

保持较低的GC开销的一些最有用的技巧是什么?

随着Java 9即将再次延迟发布,G1(“垃圾优先”)垃圾收集器将设置为HotSpot JVM的默认收集器。 从串行垃圾收集器一直到CMS收集器,JVM在其整个生命周期中都见证了许多GC实现,而G1收集器紧随其后。

随着垃圾收集器的发展,每一代(没有双关语)都会带来比以前更高的进步和改进。 串行收集器之后的并行GC利用多核计算机的计算功能使垃圾收集成为多线程。 随后的CMS(“并发标记扫描”)收集器将收集分为多个阶段,从而使许多收集工作可以在应用程序线程运行时同时完成,从而减少了“停下世界”的频率。 G1在堆非常大的JVM上增加了更好的性能,并且具有更加可预测的统一暂停。

无论高级GC收到什么,其致命弱点仍然是:冗余和不可预测的对象分配。 无论您选择使用哪种垃圾收集器,这些快速,适用,永恒的技巧将帮助您避免GC开销。

提示1:预测收集容量

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

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

让我们以以下代码为简单示例:

public static List reverse(List<? extends T> list) {List result = new ArrayList();for (int i = list.size() - 1; i >= 0; i--) {result.add(list.get(i));}return result;
}

此方法分配一个新数组,然后以相反的顺序填充另一个列表中的项目。

可能会很痛苦并且可以优化的一点是将项目添加到新列表的行。 对于每个添加项,列表都需要确保其基础数组中具有足够的可用插槽以容纳新项。 如果是这样,它将简单地将新项目存储在下一个空闲插槽中。 如果不是,它将分配一个新的基础数组,将旧数组的内容复制到新数组中,然后添加新项。 这将导致数组的多个分配,这些分配将保留在那里,以供GC最终收集。

我们可以通过在构造数组时让数组知道预计要保留多少个项来避免这些冗余分配:

public static List reverse(List<? extends T> list) {List result = new ArrayList(list.size());for (int i = list.size() - 1; i >= 0; i--) {result.add(list.get(i));}return result;}

这使得ArrayList构造函数执行的初始分配足够大,可以容纳list.size()项,这意味着在迭代过程中不必重新分配内存。

Guava的集合类更进一步,使我们可以使用预期项目的确切数量或估计值来初始化集合。

List result = Lists.newArrayListWithCapacity(list.size());
List result = Lists.newArrayListWithExpectedSize(list.size());

前者用于以下情况:我们确切知道集合将要容纳多少项,而后者则分配一些填充以解决估计误差。

提示2:直接处理流

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

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

然后可以将得到的字节数组解析为XML文档,JSON对象或Protocol Buffer消息,以列举一些常用的选项。

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

但是,即使数据的大小似乎是可管理的,使用上述模式在垃圾回收时也可能会导致大量开销,因为它会在堆上分配相对较大的blob来保存文件数据。

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

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

提示3:使用不可变对象

不变性具有很多优势。 甚至不让我开始。 但是,很少受到关注的一个优点是它对垃圾回收的影响。

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

public class ObjectPair {private final Object first;private final Object second;public ObjectPair(Object first, Object second) {this.first = first;this.second = second;}public Object getFirst() {return first;}public Object getSecond() {return second;}}

实例化上面的类会导致一个不可变的对象-它的所有字段都标记为final,并且不能在构造后进行修改。

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

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

提示4:警惕字符串连接

在任何基于JVM的应用程序中,字符串可能是最流行的非原始数据结构。 但是,它们隐含的重量和易于使用的特性使它们很容易成为导致应用程序占用大量内存的罪魁祸首。

问题显然不在于文字字符串,因为它们是内联和插入的,而是在于在运行时分配和构造的字符串。 让我们看一下动态字符串构造的简单示例:

public static String toString(T[] array) {String result = "[";for (int i = 0; i < array.length; i++) {result += (array[i] == array ? "this" : array[i]);if (i < array.length - 1) {result += ", ";}}result += "]";return result;
}

这是一个不错的方法,它接受一个数组并为其返回字符串表示形式。 在对象分配方面也是如此。

很难理解所有这些语法糖,但是幕后的实际情况是:

public static String toString(T[] array) {String result = "[";for (int i = 0; i < array.length; i++) {StringBuilder sb1 = new StringBuilder(result);sb1.append(array[i] == array ? "this" : array[i]);result = sb1.toString();if (i < array.length - 1) {StringBuilder sb2 = new StringBuilder(result);sb2.append(", ");result = sb2.toString();}}StringBuilder sb3 = new StringBuilder(result);sb3.append("]");result = sb3.toString();return result;
}

字符串是不可变的,这意味着在进行串联时它们本身不会被修改,而是依次分配新的字符串。 另外,编译器利用标准的StringBuilder类来实际执行这些串联。 这会带来双重麻烦,因为在循环的每次迭代中,我们都将获得(1)临时字符串的隐式分配,以及(2)临时StringBuilder对象的隐式分配,以帮助我们构造最终结果。

避免这种情况的最佳方法是显式使用StringBuilder并直接附加到其上,而不是使用有些天真的串联运算符(“ +”)。 可能是这样的:

public static String toString(T[] array) {StringBuilder sb = new StringBuilder("[");for (int i = 0; i < array.length; i++) {sb.append(array[i] == array ? "this" : array[i]);if (i < array.length - 1) {sb.append(", ");}}sb.append("]");return sb.toString();
}

在此方法的开头,我们仅分配了一个StringBuilder。 从那时起,所有字符串和列表项都附加到唯一的StringBuilder上,该字符串最终使用其toString方法仅转换为一个字符串,然后返回。

提示5:使用专门的原始集合

Java的标准集合库既方便又通用,允许我们使用具有半静态类型绑定的集合。 如果我们要使用例如一组字符串(Set <String>),或一对和一组字符串之间的映射(Map <Pair,List <String >>),则这是很棒的。

真正的问题始于我们要保存一个int列表或一个double类型值的映射。 由于泛型类型不能与基元一起使用,因此替代方法是使用装箱的类型,因此,我们需要使用List <Integer>来代替List <int>。

这是非常浪费的,因为Integer是一个完整的Object,充斥着12字节的对象标头和一个内部4字节的int字段来保存其值。 每个Integer项的总和为16个字节。 这是相同大小的原始整数列表的大小的4倍! 但是,更大的问题是所有这些Integer实际上都是对象实例,在垃圾回收期间需要考虑这些实例。

为了解决这个问题,我们在塔基皮(Takipi)使用了出色的Trove收藏库。 Trove放弃了一些(但不是全部)泛型,转而使用专门的内存有效的原始集合。 例如,不是浪费的Map <Integer,Double>,而是TIntDoubleMap形式的专门替代方法:

TIntDoubleMap map = new TIntDoubleHashMap();
map.put(5, 7.0);
map.put(-1, 9.999);
...

Trove的基础实现使用基本数组,因此在操作集合时不会进行装箱(int-> Integer)或拆箱(Integer-> int),并且不会存储任何对象来代替基元。

最后的想法

随着垃圾收集器的不断发展,以及运行时优化和JIT编译器变得越来越智能,我们作为开发人员将发现自己对如何编写GC友好代码的关注越来越少。 但是,就目前而言,不管G1多么先进,我们仍然可以做很多工作来帮助JVM。

翻译自: https://www.javacodegeeks.com/2015/12/5-tips-reducing-java-garbage-collection-overhead.html

java timer开销

java timer开销_减少Java垃圾收集开销的5条提示相关推荐

  1. java多线程编程_《java多线程编程实战指南》读书笔记 -- 基本概念

    展开 并发:多个线程操作相同资源,保证线程安全,合理使用资源 高并发:服务能同时处理多个请求,提高程序性能 测试上下文切换工具 Lmbench3 测量上下文切换时长 vmstat 测量上下文切换次数 ...

  2. java初始化数据报_初始化java原因

    虚拟机的类加载机制 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类 即虚拟机的类加载机制. 在Java中,类型的加载.链接 ...

  3. java核心教程_核心Java教程

    java核心教程 Welcome to Core Java Tutorial. I have written a lot on Core Java and Java EE frameworks. Th ...

  4. java 包命名_【Java】包的命名规则

    命名规范 包:所有单词的字母小写,之后每个单词用-隔开,如 org.nemo.demo 常量:所有单词的字母大写,之后每个单词用_隔开,如 FLAG 类:所有单词的首字母大写,如 TestJava p ...

  5. java web源代码_检测Java Web应用程序而无需修改其源代码

    java web源代码 与其他系统进行交互时,大多数Java Web应用程序都使用标准Java接口. 诸如Web页面或REST服务器之类的基于HTTP的服务是使用接口javax.servlet.Ser ...

  6. java高级教程_高级Java教程

    java高级教程 课程大纲 学习Java基础很容易. 但是,真正钻研该语言并研究其更高级的概念和细微差别将使您成为一名出色的Java开发人员. 网络上充斥着"软","便宜 ...

  7. java开发错误_每个Java开发人员都必须避免的9个安全错误

    java开发错误 Checkmarx CxSAST是功能强大的源代码分析(SCA)解决方案,旨在从根本上识别,跟踪和修复技术和逻辑安全漏洞:源代码. 在这里查看 ! 自从1995年中期引入Java以来 ...

  8. java正则表达式 匹配()_学习Java正则表达式(匹配、替换、查找)

    import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public c ...

  9. 纯java分布式内存数据库_最新Java岗面试清单:分布式+Dubbo+线程+Redis+数据库+JVM+并发...

    最近可能有点闲的慌,没事就去找面试面经,整理了一波面试题.我大概是分成了Java基础.中级.高级,分布式,Spring架构,多线程,网络,MySQL,Redis缓存,JVM相关,调优,设计模式,算法与 ...

最新文章

  1. 《解释性机器学习》笔记(五):Rule Fit 规则拟合
  2. Java 实现第三方 QQ 账号登录
  3. 快速提升网站收录量的技巧有哪些?
  4. mysql 账户管理_Mysql账户管理_MySQL
  5. matlab 工业相机 曝光时间_CCD高清工业相机的主要参数及应用
  6. python getattrribute_python操作对象属性
  7. 用啥Selenium?! .NET程序员就用自家的Playwright for .NET
  8. 基于jsp+mysql的JSP在线水果销售商城系统设计实现
  9. 在linux下用tomcat部署java web项目的过程与注意事项(转)
  10. sql语法:inner join on, left join on, right join on具体用法
  11. [渝粤教育] 西南科技大学 工程测量 在线考试复习资料(1)
  12. size ar objdump readelf binutils
  13. netstat 查看网络状态
  14. jszip在线解压压缩文件
  15. 乐心健康php,《乐心健康》修改步数网页【获取方法】入口
  16. MSP430指令初探
  17. seleinum+requets 下载歌曲
  18. 突然有一天,我老无所依
  19. 国防科技大学计算机考纲,国防科技大学2022年硕士研究生考试大纲-F1007雷达原理...
  20. 让你的网页,点哪都是小心心~~~

热门文章

  1. codeforces:CF750 复盘
  2. 警卫站岗(树上dp)
  3. 牛客挑战赛43C-最优公式【二分】
  4. jzoj2941-贿赂【数学期望,dfs】
  5. POJ3348-Cows【凸包,计算几何】
  6. 【2018.4.14】模拟赛之二-ssl2392 蚂蚁【图论】
  7. P2814-家谱【图论,并查集,std map库】
  8. 各种有用的东西留言板
  9. jsdiff 比较文本内容差异
  10. Http 持久连接与 HttpClient 连接池