深入理解java虚拟机

152 阅读3分钟
原文链接: click.aliyun.com

说在前面的话
<深入理解java虚拟机> link 是一本jvm入门经典,推荐所有java工程师阅读,并应该多读,不同阶段读. 这篇博客就是为了总结本人从该书中的领悟.

运行时的数据分区


image

  • 程序计数器:线程执行的字节码行号.
  • 虚拟机栈:生命周期和线程相同,描述的是java方法执行的内存模型, 每个方法执行的同时都会创建一个栈帧(stack frame).存放局部变量、操作数栈、动态链接、方法出口等信息.
  • 堆:存放对象实例.随着JIT编译器、逃逸分析技术,栈上分配、标量替换导致不一定所有的对象都在堆上.
  • 方法区:虚拟机加载的类信息、常量、静态变量、JIT编译的代码。堆的一个逻辑部分。hotspot使用永久代实现方法区,会受限于MaxPermSize
  • Direct Memory: DirectByteBuffer分配堆外内存.不受-Xmx参数控制.

java对象
java 对象的组成: 对象头(header)+实例数据(instance data)+对齐填充

  • 对象头:类的元数据、hash码、gc分代).
  • 实例数据:包括父类和自身定义的字段内容.
  • 对齐填充,保证8字节的整数倍.

对象的访问定位,通过栈上的reference操作堆.
image

内存/栈溢出

  • heap oom,-Xmx -Xms

     Memory Leak:内存无法被回收,GC Roots引用链.
     Memory overflow:调大内存,优化代码.
  • stack overflow(虚拟机栈和本地方法栈)

     栈深度支持到1000-2000,如果超过会导致stackoverflow.
     如果线程数量过多,则会报错out of memory(受限于一个进程可用内存-堆内存-方法区内存),每个线程需要分配一个栈容量,参数为-Xss.
  • 方法区和运行时常量池的溢出

     String.intern :常量池.
     方法区,除了已加载的类,还有许多动态生成类,如cglib、动态代理、osgi、jsp等。
  • 本机直接内存directMemory

     用反射操作unsafe.
    

GC


对象存活的判定算法:判断对象和GCROOT引用链是否可达来判断对象是否已死. GC root包括:

  • 虚拟机栈的本地变量表引用的对象.
  • 方法区中的静态变量引用的对象.
  • 方法区中常量池引用的对象.
  • 本地方法栈JNI引用的对象

引用的分类, 由强到弱:

标题1 标题2 标题3
强引用 Object o = new Object 永远不会回收被引用的对象
软引用 SoftReference 在内存溢出前被回收
弱引用 WeakReference 活到下一次gc.
虚引用 PhantomReference 无法通过引用获取,在被回收时收到通知

方法区的GC,回收效率低,效果差:

  • 无效的常量,如"abc"进入了方法区,但没有地方引用.
  • 无效的类信息, 如所有实例被回收/classLoad卸载.
  • 反射生成的类, 需要虚拟机具备类卸载功能.

垃圾收集算法:

  • 标记清除算法. 产生空间碎片,标记和清除的效率都比较低.
  • 标记复制算法. 内存可用空间降低, 新生代使用,由于大部分对象朝生夕死, 不需要1:1,一般是8:1:1.
  • 标记整理算法. 老生代使用.