(翻译www.java-performance.com)提高Java程序性能--JDK篇(一)

  关键词: hash map, FastUtil, GS collections, HPPC, Koloboke, Trove.

Large HashMap overview: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove - January 2015 version: a quick overview of all major libraries implementing hashmaps.

Tags: hash mapFastUtilGS collectionsHPPCKolobokeTrove.

  • FastUtil 6.6.0 turned out to be consistently fast. It may become even faster if it would introduce any other storage structures except 2 arrays for keys and values.
  • Koloboke is getting second in many tests, but it still outperforms FastUtil in int-int tests.
  • GS implementation is good enough, but is slower than FastUtil and Koloboke.
  • JDK maps are pretty good for Object-Object maps provided that you can tolerate the extra memory consumption and you will call HashMap constructor with required capacity =actual_capacity / fill_factor + 1 to avoid rehashing.
  • Trove suffers from using mod operation for array index calculations and HPPC is too slow due to an extra underlying array (for cell states).

-------------- 译 ---------------

Large HashMap overview: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove - January 2015 version: 实现了hashmap的主流库速览。

1. FastUtil 6.6.0 还是一贯地快。如果不是使用了2个数组储来存键与值而采用其他存储结构,它还可能更快。
2. Koloboke 在很多测试中都排到第二,但是在键值类型为int-int的测试时胜过FastUtil。
3. JDK 在对象-对象型(Object-Object)map中表现得相当不错。这需要你忍受额外的内存消耗,额外的消耗是因为在构造HashMap时会需要 实际容量/填充因子 + 1 (capacity = actual_capacity / fill_factor + 1)以避免再次散列。
4. Trove 遭受着在数组下标计算中使用取模运算(mod)的诟病,而且为了保存数组元素状态使用了一个隐含的额外数组使得HPPC非常的慢。(HPPC http://labs.carrotsearch.com/hppc.html)


 关键词:金融,货币,HFT,低时延

Using double/long vs BigDecimal for monetary calculationsdoublelongjava.math.BigDecimaljava.lang.String:

Tags: financemoneyHFTlow latency.

  • If you want to implement fast and correct monetary arithmetic operations in Java, stick to the following rules:
    1. Store monetary values in the smallest currency units (for example, cents) in long variables.
    2. Avoid working with non-integral values while using double (calculate in the smallest currency units).
    3. Add/subtract using long.
    4. Round any multiplication/division results using Math.round/rint/ceil/floor (per your system requirements).
    5. Your calculations should fit into 52 bits (double precision).
  • Always use MathContext for BigDecimal multiplication and division in order to avoid ArithmeticException for infinitely long decimal results. Don't use MathContext.UNLIMITED for that reason - it is equivalent to no context at all.
  • Do not convert double to BigDecimal, instead convert String to BigDecimal when possible.

-------------- 译 ---------------

如果你想在java中关于货币的算术运算快速与正确地实现,请坚持下面的法则:
1. 将货币值以其最小单位(比如美元中的美分)存入long变量中。
2. 使用double型时避免计算的值含有小数部分(通过使用最小的货币单位来实现)。
3. 用long类型进行加/减。
4. 对任何乘/除结果的四舍五入,(按照你的系统之需求)使用Math.round/rint/ceil/floor。
5. 你的计算结果应该能够在52bits内表示(double类型的精度)。
为了避免计算结果为无穷时产生ArithmeticException,请在BigDecimal的乘除运算时始终使用MathContext。不要使用MathContext.UNLIMITED,理由是它等于没有任何上下文。
不要从double转换到BigDecimal,可能的话用String转换到BigDecimal来替代。

 


关键词: String.substring, Java 7,内存消耗,低时延

Changes to String internal representation made in Java 1.7.0_06java.lang.Stringjava.util.HashMapjava.util.Hashtablejava.util.HashSetjava.util.LinkedHashMap,java.util.LinkedHashSetjava.util.WeakHashMap and java.util.concurrent.ConcurrentHashMap:

Tags: String.substringJava 7memory consumptionlow latency.

  • From Java 1.7.0_06 String.substring always creates a new underlying char[] value for every String it creates. This means that this method now has a linear complexity compared to previous constant complexity. The advantage of this change is a slightly smaller memory footprint of a String (8 bytes less than before) and a guarantee to avoid memory leaks caused by String.substring (see String packing part 1: converting characters to bytes for more details on Java object memory layout).
  • Java 7u6+ functionality. Removed in Java 8. Starting from the same Java update, String class got a second hashing method called hash32. This method is currently not public and could be accessed without reflection only via sun.misc.Hashing.stringHash32(String) call. This method is used by 7 JDK hash-based collections if their size will exceedjdk.map.althashing.threshold system property. This is an experimental function and currently I don't recommend using it in your code.
  • Java 7u6 (inclusive) to Java 7u40 (exclusive) functionality. Not applicable to Java 8. All standard JDK non-concurrent maps and sets in all Java versions between Java 7u6 (inclusive) and Java 7u40 (exclusive) are affected by a performance bug caused by new hashing implementation. This bug affects only multithreaded applications creating heaps of maps per second. See this article for more details. This problem was fixed in Java 7u40.

-------------- 译 ---------------

1. 自从Java 1.7.0_06起,String.substring总是会为每个它所创建的String创建一个隐含的char数组。这意味着对比以前的常数复杂度,现在String.substring会是线性复杂度。这样修改的优势是String的内存占用要稍微少点(比以前少8字节),且保证消除String.substring引起的内存泄漏。
2. Java 7u6+的功能,在Java 8中已移除。从此Java的更新起,在String类中新增了一个叫hash32的散列函数。这个函数目前不是公有的,只能经由sun.misc.Hashing.stringHash32(String)轻率地调用到。在JDK7中若基于散列的集合的大小超过jdk.map.althashing.threshold系统属性时会使用到这个函数。这只是一个实验性的函数,目前我不推荐你使用它。
3. 从Java 7u6到Java 7u40所独有的功能,在Java 8中已不可用。Java 7u6到Java 7u40之间的所有Java版本的所有标准JDK中的非并发(non-concurrent)map和set都会被一个由新的散列实现引起的性能BUG所影响。这个BUG只有在多线程程序在秒内产生多个map的堆时才会产生影响。这个问题已在Java 7u40中修复。


关键词:字符串,Java 8,内存消耗

 

String deduplication feature (from Java 8 update 20): this article will describe the string deduplication feature added in Java 8 update 20. It will allow you to save memory occupied by the duplicate strings without writing a single line of Java code. While this is not the most efficient memory saving tool in the absolute values, it is definitely a winner in the achievement vs developer efforts nomination.

Tags: StringJava 8memory consumption.

  • String deduplication feature was added in Java 8 update 20. It is a part of G1 garbage collector, so it should be turned on with G1 collector: -XX:+UseG1GC -XX:+UseStringDeduplication
  • String deduplication is an optional G1 phase. It depends on the current system load.
  • String deduplication is looking for the strings with the same contents and canonicalizing the underlying char[] with string characters. You don't need to write code to use this feature, but it means you are being left with distinct String objects, each of those occupying 24 bytes. Sometimes it worth to intern strings explicitly using String.intern.
  • String deduplication does not process too young strings. The minimal age of processed strings is managed by -XX:StringDeduplicationAgeThreshold=3 JVM parameter (3 is the default value of this parameter).

-------------- 译 ---------------

(http://java-performance.info/java-string-deduplication/)网址中的这篇文章叙述了Java 8 update 20中新增的相同字符串只存一份的特性。这个特性允许在有多份相同字符串的情况下节省内存占用,且你不需要写一行代码。当然这并不是绝对情况下最有效的内存节省工具,但它绝对是achievement vs developer efforts nomination中的赢家。

译注:deduplication表示去除多份相同的数据只保留一份的意思,这里我还没找到一个合适的词来表达,所以在文中直接使用英文单词。
1. String deduplication特性是在Java 8 update 20中所增加的。此特性是G1垃圾收集器的一部分,所以要使用此特性必须使用G1。方法:加上参数 -XX:+UseG1GC -XX:+UseStringDeduplication
2. String deduplication是G1的一个可选时期,它依赖于当前的系统负载。
3. String deduplication寻找具有相同内容的字符串并以字符串中的字符来规范化隐含的char数组(the underlying char[])。你并不需要通过写代码来使用这个特性,但这意味着只有不相同的字符串对象会被留下,每个对象占用24字节。有时值得留下完整的字符串,这时可以通过显示的调用String.intern来实现。
4. String deduplication不会处理太年轻的字符串,可被处理字符串的最小年纪由Java虚拟机的-XX:StringDeduplicationAgeThreshold=3参数来指定,前面的3是一个默认参数。(译注: 这里的年纪等相关词汇请参考Java垃圾收集器的关于各处理时期的相关概念


关键词:java中的序列化,java中的不安全内存访问,高吞吐率,低时延

Performance of various methods of binary serialization in Javajava.nio.ByteBuffersun.misc.Unsafejava.io.DataInputStreamjava.io.DataOutputStream,java.io.ByteArrayInputStreamjava.io.ByteArrayOutputStream: comparison of binary serialization performance using various classes:

Tags: serialization in Javaunsafe memory access in Javahigh throughputlow latency.

  • It is extremely slow to write single bytes to direct byte buffers. You should avoid using direct byte buffers for writing records with mostly single byte fields.
  • If you have primitive array fields - always use bulk methods to process them. ByteBuffer bulk methods performance is close to those of Unsafe (though ByteBuffer methods are always a little slower). If you need to store/load any other primitive array except byte - use ByteBuffer.to[YourType]Buffer.put(array) method call followed by your byte buffer position update. Do not call ByteBuffer.put[YourType] method in a loop!
  • The higher your average field length - the slower is a heap buffer and the faster is a direct byte buffer. Unsafe access even to separate fields is still faster.
  • In Java 7 many types of ByteBuffer accesses were seriously optimized compared to Java 6.
  • Always try to serialize primitive arrays using direct byte buffer with your platform native byte order - its performance is very close to Unsafe performance and it is portable, unlikeUnsafe code.

-------------- 译 ---------------

1. 以单字节方式往直接字节缓冲区(direct byte buffers,注1)中写数据是相当慢的。尽量避免往直接字节缓冲区中写含有大量单字节字段(成员变量)的记录(records,一条条数据)。
2. 如果对象字段中含有元素是基础类型的数组,尽量用块操作函数(bulk methods)来处理。尽管比起那些Unsafe来说稍微慢了一点,但ByteBuffer的块操作函数的性能还是接近它们的。如果你想存取除byte类型外的其他基础类型数组,调用ByteBuffer.to[你的类型]Buffer.put(array)。不要在循环中调用ByteBuffer.put[你的类型]。
3. 对象中各字段的平均长度越大,那么使用堆缓冲区(heap buffer)就越慢,使用直接缓冲区(direct byte buffer)就越快。Unsafe,即使是分开访问各字段仍然更快些。
4. 对比Java 6,在Java 7中ByteBuffer对许多类型的访问都做了认真的优化。
5. 总是尝试以使用本地字节序的直接缓冲区来序列化基础类型数组--这样性能就非常接近Unsafe的性能了,并且不像Unsafe代码那样,这样是具有可移植性的。
注1:水平较低,不知道这个概念怎么翻译。有兴趣的可以自己百度,它大概的意思就是这种缓冲区尽量表现出让本地IO直接访问它,而不需要一个中间缓冲。所以它有两个特点,一是快,二是GC无法管理它。
注2:Unsafe 这又是一个我没法形容的词,我的理解是使用不安全的操作去访问缓冲区,所以请自己用合适的词去替代文中的Unsafe

----the end----

posted on 2016-07-20 18:48  哥斯达黎加  阅读(461)  评论(0编辑  收藏  举报

导航