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

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开销

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

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

相关文章

C++STL Vector

#include<iostream> #include<vector> using namespace std;int main() {//vector构造函数vector<int>A(); //无参数&#xff0c;构造一个空的vectorvector<int>B(10, 1);//数量(num)和值(val) - 构造一个初始放入num个值为val的元素的Vector vector&…

java serializable 源码_Java源码分析之-Serializable接口

一、基本概念Serializable接口&#xff1a;1. 实现了Serializable接口的类&#xff0c;可以进行序列化和反序列化&#xff1b;没有实现这个接口的类的任何(state)状态/域或者属性值不能被序列化2. All subtypes of a serializable class are themselves serializable. 所有实现…

gluon_带有Gluon Ignite和Dagger的JavaFX中的依赖注入

gluon依赖注入抽象框架Gluon Ignite在几个流行的依赖注入框架&#xff08;例如Spring&#xff0c;Dagger和Guice&#xff09;上创建了一个通用抽象。 目前&#xff0c;Gluon 页面仅包含一个示例&#xff0c;该示例使用Gluon Ignite和Google Guice作为依赖注入框架&#xff0c;…

快速排序 自带时间复杂度检测

//时间复杂度 O(N*log2N //稳定性&#xff1a;不稳定 //来源于分治思想 /* 确定分界点 调整区间 递归处理两端算法思想&#xff0c;快排是基于冒泡排序的优化&#xff0c;冒泡排序从一侧开始进行&#xff0c;而快排是两边同时进行从而时间复杂度折半&#xff0c;同时包含了二…

java arraylist 常用方法_分享ArrayList中的几个常用方法的源码

jdk1.7.0_79上文里解析了有关ArrayList中的几个常用方法的源码——《有关ArrayList常用方法的源码解析》&#xff0c;本文将对LinkedList的常用方法做简要解析。LinkedList是基于链表实现的&#xff0c;也就是说它具备了链表的优点和缺点&#xff0c;随机访问慢、插入删除速度快…

java8 streams_Java 8 Streams API:对流进行分组和分区

java8 streams这篇文章展示了如何使用Streams API中可用的Collectors将具有groupingBy的流元素和具有partitioningBy的流元素进行groupingBy 。 考虑一系列Employee对象&#xff0c;每个对象都有名称&#xff0c;城市和销售数量&#xff0c;如下表所示&#xff1a; ---------…

【FFMPEG应用篇】使用FFMPEG解析H264编码为YUV格式

头文件 #pragma once#ifndef _VIDEO_DECODING_HEADER_ #define _VIDEO_DECODING_HEADER_#define INBUF_SIZE 4096 #define AUDIO_INBUF_SIZE 20480 #define AUDIO_REFILL_THRESH 4096extern "C" { #include "libavutil/opt.h" #include "libavcodec/…

java 反射api_Java学习笔记--反射API

反射API1.反射API的介绍通过反射API可以获取Java程序在运行时刻的内部结构。比如Java类中包含的构造方法、域和方法等元素&#xff0c;并可以与这些元素进行交换。按照 一般地面向对象的设计思路&#xff0c;一个对象的内部状态都应该通过相应的方法来改变&#xff0c;而不是直…

c++各类型有效范围

int -2147483648 ~ 2147483647 float 有6-7位有效数字 double 有15-16位有效数字

couchbase_具有Couchbase,Java EE和WildFly的CRUD Java应用程序

couchbaseCouchbase是一个开源的NoSQL文档数据库。 它允许访问&#xff0c;索引和查询JSON文档&#xff0c;同时利用集成的分布式缓存来实现高性能数据访问。 开发人员可以使用不同的语言&#xff08;Java&#xff0c;Go&#xff0c;.NET&#xff0c;Node&#xff0c;PHP&…

java 对象的态_Java面向对象-------多态总结

1.多态&#xff1a;是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作&#xff0c;如图所示&#xff1a;多态性是对象多种表现形式的体现。2.多态作用&#xff1a;1. 消除类型之间的耦合关系2. 可替换性3. 可扩充性4…

C++ 【随想录】(六)C++后端向JS前端转换

需要包含的头文件 #include"comutil.h" #include"<string> BSTR转string BSTR bstrText ::SysAllocString(L"Test"); string str _com_util::ConvertBSTRToString(bstrText); SysFreeString(bstrText); string转BSTR…

netbeans连接数据库_NetBeans Java EE技巧#1 –数据库中的实体类

netbeans连接数据库NetBeans IDE是开发各种应用程序的绝佳选择。 具体来说&#xff0c;我每天都使用它来开发和维护Java EE应用程序。 在过去的几个发行版中&#xff0c;不仅Java EE的生产力提高了&#xff0c;而且NetBeans IDE还减少了开发应用程序的时间……使Java EE和NetBe…

java spring eventbus_spring集成guava的event bus

Guava的event busguava&#xff0c; https://github.com/google/guava 是一个非常有名的Java类库&#xff0c;提供了很多在日常开发中常用的集合、函数接口等。此外&#xff0c;guava还提供了一个模块叫做event bus&#xff0c;生产者往event bus上投递消息&#xff0c;event b…

LeetCode 66 加一

原题链接 关键词 &#xff1a;数组 模拟法 解题思路: 此题为处理数组&#xff0c;数组特点 --数组下标 可以前后覆盖 数组元素可交换等。 此题分为三种情况 1.传入数为234形式 &#xff0c;加一后无进位。直接在最后一个数组1即可 2.传入数为239形式&#xff0c;即…

jboss fuse 教程_使用JBoss Fuse和OpenShift进行Cloud Native Camel骑行

jboss fuse 教程红帽公司最近发布了一个微服务集成工具包&#xff0c;用于在OpenShift v3的Docker和Kubernetes环境中运行我们的微服务。 为了帮助人们更好地理解这一点&#xff0c;我将Rider Auto应用程序迁移到一组微服务中&#xff0c;该应用程序已经存在了一段时间&#xf…

java streamhandler_java中的Lamdba表达式和Stream

基于JDK 1.81、循环&#xff1a;// 以前的循环方式for (String player : players) {System.out.print(player "; ");}// 使用 lambda 表达式以及函数操作(functional operation)players.forEach((player) -> System.out.print(player "; "));// 在 Ja…

LeetCode 217 存在重复元素

原题链接 关键词 数组 排序 重复 解题思路 1.先把数组进行排序&#xff0c;然后比较是否有相邻的两个数字相等 ,有返回TRUE 无则返回FALSE 2.需要注意的是nums.size()-1 因为是从下标0开始&#xff0c;所以要-1&#xff0c;否则会造成数组越界 class Solution { pu…

java 代码运行速度慢_C代码的运行速度总是比Java快,对吧? 错误!

java 代码运行速度慢因此&#xff0c;我们都知道&#xff0c;Java解释缓慢且C的编译和优化运行速度非常快。 您可能知道&#xff0c;情况截然不同。 TL; DR Java适用于星座图&#xff0c;它的速度更快&#xff0c;在JIT上可以执行内联&#xff0c;因为所有方法/功能都是可见的…

LeetCode 26.删除排序数组中的重复项

原题 标签 &#xff1a; 数组 重复元素 有序 双指针 快慢指针 解题思路: 1.判断数组是否为空&#xff0c;为空返回0 2.我们可以放置两个指针 &#xff0c;其中 k 是慢指针&#xff0c;用来存下所有不同的数&#xff0c;而 j 是快指针用来扫描整个数组。只要 nums[i] n…