本文是深入理解Java虚拟机-读书笔记系列的文章,查看所有文章目录可以点击该超链接查看。

1. 高性能硬件上的程序部署策略

一个PV比较大的网站,有时候长时间失去响应,这是由于GC造成。JVM默认使用PS这种吞吐量优先收集器,一次full GC停顿高达14秒。程序设计导致大量较大的文档数据驻留在内存。

心得:如果full GC比较少、绝大多数对象的存活时间都比较短,才建议使用较大的堆。否则GC的开销会比较大,有较长的时间停顿。

2. 集群间同步导致的内存溢出

由于集群同步大量数据,导致数据过多,而超过内存限制。

心得:

  1. 发生OOM的时候自动生成堆dump文件可以加参数: -XX:+HeapDumpOnOutOfMemoryError

3. 堆外内存导致的溢出错误

当出现OOM的时候不得不考虑下堆外内存导致的错误。第2章的时候也提到过,出现堆外内存导致的OOM时,heap dump是无效的。异常栈里面可以明显看到DirectByteBuffer、NIO等字样的时候就可以考虑是堆外内存溢出导致的问题了。

最佳实践:因此总内存在分配给JAVA堆的时候,也确保预留了足够的堆外内存。

重点知识:

  1. 堆外内存不足的时候不会执行主动触发GC,只会抛一些OOM的错误。
  2. 堆外内存导致的OOM,heap dump是无效的
  3. 只能等老年代full GC的时候顺便回收下堆外空间

其他心得:
如果内存不足时抛出StackOverflowError(纵向上,无法分配栈帧)或者OutOfMemoryError:unable to create native thread(横向上,无法建立新的线程),考虑增加-Xss

4. 外部命令导致系统缓慢

避免在Java中使用Runtime.getRuntime().exec()方法来执行外部的shell脚本。这样的实现方式开销较大。如果频繁调用会给CPU和内存带来极大压力。

JVM执行这个命令的过程:

  1. 克隆一个和当前虚拟机拥有一样环境变量的进程
  2. 勇这个心得进程去执行外部命令,最后再退出这个进程。

5. 服务器JVM进程崩溃

使用异步调用,但是被调用方和调用发速度不匹配导致大量等待的线程和socket连接。最后积压太多超过JVM承受能力使得虚拟机进程崩溃。

解决办法: 采用生产者、消费者模式的消息队列来实现

6. 不恰当数据结构导致内存占用过大

例如: HashMap这个种类型,各种头部开销比内容(两个Long类型)开销都要大得多,导致空间利用率低。

7. 由Windows虚拟内存导致的长时间停顿

GUI程序最小化的时候,他的工作内存被自动交换到磁盘的页面文件之中,这样发生GC时就又可能因为恢复页面文件的操作而导致不正常的GC停顿。

解决办法:加入参数"-Dsum.awt.keepWorkingSetOnMinimize=true"

8. 实战:eclipse运行速度调优

这个整个过程较长,可以看书P142页开始。 我这里仅仅记录下一些值得注意的点:

  1. hostSpot虚拟机名字由来: 如果一段JAVA方法被调用次数达到一定程度,就会判断为热带吗交给JIT编译器(Just in time compiler)编译为本地代码,提高运行速度。
  2. visualVM里面的visualGC插件还是很有用的