Java8 Striped64 和 LongAdder

数据 striping

根据维基百科的这段说明

In computer data storage, data striping is the technique of segmenting logically sequential data, such as a file, so that consecutive segments are stored on different physical storage devices.

Striping is useful when a processing device requests data more quickly than a single storage device can provide it. By spreading segments across multiple devices which can be accessed concurrently, total data throughput is increased. It is also a useful method for balancing I/O load across an array of disks. Striping is used across disk drives in redundant array of independent disks (RAID) storage, network interface controllers, different computers in clustered file systems and grid-oriented storage, and RAM in some systems.

数据 striping 就是把逻辑上连续的数据分为多个段,使这一序列的段存储在不同的物理设备上。通过把段分散到多个设备上可以增加访问并发性,从而提升总体的吞吐量。

Striped64

JDK 8 的 java.util.concurrent.atomic 下有一个包本地的类 Striped64 ,它持有常见表示和机制用于类支持动态 striping 到 64bit 值上。

设计思路

这个类维护一个延迟初始的、原子地更新值的表,加上额外的 “base” 字段。表的大小是 2 的幂。索引使用每线程的哈希码来masked。这个的几乎所有声明都是包私有的,通过子类直接访问。

表的条目是 Cell 类,一个填充过(通过 sun.misc.Contended )的 AtomicLong 的变体,用于减少缓存竞争。填充对于多数 Atomics 是过度杀伤的,因为它们一般不规则地分布在内存里,因此彼此间不会有太多冲突。但存在于数组的原子对象将倾向于彼此相邻地放置,因此将通常共享缓存行(对性能有巨大的副作用),在没有这个防备下。

部分地,因为Cell相对比较大,我们避免创建它们直到需要时。当没有竞争时,所有的更新都作用到 base 字段。根据第一次竞争(更新 base 的 CAS 失败),表被初始化为大小 2。表的大小根据更多的竞争加倍,直到大于或等于CPU数量的最小的 2 的幂。表的槽在它们需要之前保持空。

一个单独的自旋锁(“cellsBusy”)用于初始化和resize表,还有用新的Cell填充槽。不需要阻塞锁,当锁不可得,线程尝试其他槽(或 base)。在这些重试中,会增加竞争和减少本地性,这仍然好于其他选择。

继续阅读

Java SE 6 故障排除指南 – 4、系统崩溃故障排除

崩溃或致命错误导致进程异常终止。有各种可能的理由导致崩溃。例如,崩溃可能是由于HotSpot VM、系统库、Java SE 库或API、程序本地代码、甚至操作系统里的 bug。极端因素如操作系统资源耗尽也可以导致崩溃。

因 HotSpot VM 或 Java SE库代码导致的崩溃是罕见的。本章提供如何检查崩溃的建议。有时候可以变通崩溃直到导致崩溃的源被诊断和修复(也就是可以避开崩溃)。

通常,崩溃的第一步是定位致命错误日志。这是HotSpot VM生成的文本文件。附录C-致命错误日志 解释了如何定位文件和文件的详细描述。

4.1 崩溃样本

本节展示一些样本来说明错误日志是如何用于启发崩溃原因的。

4.1 测定哪里发生崩溃

错误日志头显示了有问题的帧。见 C.3 格式头。

如果顶层帧是本地帧且不是操作系统本地帧,这表明问题可能发生在本地库,而不是在JVM里。解决崩溃的第一步是研究本地库发生崩溃的源。有三个选择,取决于本地库的源。

如果本地库是由你的程序提供,研究你的本地库的源代码。选项 -Xcheck:jni 可以帮助查找本地 bug。见 B.2.1 -Xcheck:jni 选项。

如果你的程序使用的本地库是由其他供应商提供,报告bug,提供致命错误日志信息。

继续阅读

Java SE 6 故障排除指南 – 5、挂起或循环进程故障排除

本章为挂起或循环进程的故障排除在特定程序上提供了信息和指导。

问题在涉及挂起或循环进程时发生。挂起可能因为多种原因发生,但经常是源于程序代码、API代码或库代码里的死锁。挂起甚至是因为 HotSpot VM的bug。

有时候,一个表面上是挂起的可能是个循环。例如,VM进程里的bug导致一个或多个线程进入死循环,会消耗掉所有可得CPU周期。

诊断挂起的最初步骤是找出VM进程是空闲还是消耗了所有可得CPU周期,为做这个要求使用操作系统工具。如果进程表现为繁忙且消耗了所有可得CPU周期,那么问题很可能是循环线程而不是死锁。

继续阅读

Java SE 6 故障排除指南 – 3、内存泄露

内存泄露故障排除

如果你的应用程序执行的时间越来越长,或如果操作系统执行越来越慢,这可能是内存泄露的指示。换句话说,虚拟内存被分配但在不需要时没有归还。最终应用程序或系统没有可用内存,应用程序非正常终止。

这篇文章提供了一些涉及内存泄露的问题诊断的建议。

3.1 OutOfMemoryError 的含义

一个最常见的内存泄露的指示是 java.lang.OutOfMemoryError 错误。这个错误在Java堆或堆的特定区域没有足够空间用于分配对象时抛出。垃圾收集器不能创造更多可用空间来容纳一个新的对象,堆也不能扩展。

当 java.lang.OutOfMemoryError 错误抛出时,栈轨迹也会被打印。

java.lang.OutOfMemoryError 也可以被本地库代码抛出,当本地分配不能满足时,例如,交换空间很低。

诊断 java.lang.OutOfMemoryError 的一个早期步骤是确定错误的含义。它是否意味着Java堆满了,或意味着本地堆满了?为帮助你回答这个问题,下面的子章节解释了一些可能的错误信息,还有连接到更详细信息的链接:

继续阅读

Java SE 6 故障排除指南 – 1、诊断工具和选项

英文完整文档:http://www.oracle.com/technetwork/java/javase/tsg-vm-149989.pdf

诊断工具和选项

这章介绍了JDK 6 和 Java SE6 里不同的诊断和监视工具。这些工具在第二章详细介绍。

附录 D 列出了这个发布里的可用工具,还有上次发布以来的改变。

注意:本章描述的一些命令行工具是实验性的。例如 jstack, jinfo, jmap 就是实验性的。这些工具可能在将来的JDK发布里改变,或者不包括在将来的发布里。

继续阅读

Java 性能调优指南 – 高性能Java

本文主要基于 Java performance tuning tips or everything you want to know about Java performance in 15 minutes的翻译。

这篇指南主要调优 java 代码而不是 JVM 设置。

一、JDK 类

Java 1.7.0_06 String 内部表示的改变

  • 从 Java 1.7.0_06 开始,String.substring 总是为它创建的新字符串创建一个新的底层 char[] 值。这意味着这个方法现在有线性的复杂度,之前是常量的复杂度。这个改变的好处是字符串需要更少的内存 footprint(比以前少8字节),也是避免 String.substring 导致的内存泄露的一个保证。

Java 里二进制序列化的不同方法

  • 写单个字节到直接字节缓存是非常慢的。对写记录 — 大多数情况是单个字节的字段,你应该避免使用直接字节缓存。
  • 如果你有原始数组字段,总是使用bulk方法来处理它们(一次处理一批的)。ByteBuffer 的bulk 方法的性能接近于Unsafe的这些方法。如果你需要存储/加载任何其它原始数组 – 除字节型的,用 ByteBuffer.to[YouType]Buffer.put(array) 方法调用,字节缓存的位置会自动更新。不要在循环里调用 ByteBuffer.put[YouType] 方法。
  • 总是尝试用带本地字节顺序的直接缓存序列化原始数组。直接字节缓存的性能接近于Unsafe且是可移植的。

Java 集合概览

单线程 并发
Lists ArrayList : 一般基于数组
LinkedList :不要使用
Vector:废弃
CopyOnWriteArrayList:很少更新,经常遍历。
Queues/deques ArrayDeque:一般基于数组。
Stack:废弃。
PriorityQueue:有序的检索操作。
ArrayBlockQueue:有界阻塞queue。
ConcurrentLinkedDeque/ConcurrentLinkedQueue:无界链接queue(CAS)。
DelayQueue:在每个元素上带延迟的queue。
LinkedBlockingDeque/LinkedBlockingQueue:可选的有界链接queue(锁)。
LinkedTransferQueue:may transfer elements w/o storing。
PriorityBlockingQueue:并发的PriorityQueue。
SynchronousQueue:实现了Queue接口的Exchanger。
Maps HashMap:一般的map。
EnumMap:enum作为key的。Hashtable:废弃。
IdentityHashMap:用 == 比较键。
LinkedHashMap:保留了插入顺序。
TreeMap:有序的键。
WeakHashMap:可用于缓存。
ConcurrentHashMap:一般的并发map。
ConcurrentSkipListMap:有序的并发map。
Sets HashSet:一般的set。
EnumSet:enum的集合。
BitSet:比特位或稀疏整数的集合。
LinkedHashSet:保留了插入顺序。
TreeSet:有序集合。
ConcurrentSkipListSet:有序并发集合。
CopyOnWriteArraySet:很少更新,经常遍历。

继续阅读