《深入理解JVM》读书笔记(二)

学习总结

Posted by 溜大虾 on July 22, 2017

第二章 Java内存区域与内存溢出异常

运行时数据区域

Java虚拟机会把他所管理的内存划分为若干个不同的数据区域:

线程共享 线程隔离
方法区 本地方法栈
虚拟机栈
  程序计数器

1.程序计数器

1.程序计数器是当前线程所执行的字节码的行号指示器2.在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令3.如果正在执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果是native方法,则计数器值为空

2.Java虚拟机栈

1.虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量、操作数栈、动态链接、方法出口等信息2.通常所说的“Java内存分为堆内存和栈内存”是过于粗糙的,其中的栈所指的实际上是虚拟机栈,确切的说是虚拟机栈中的局部变量表3.long 和 double型的数据会占用2个局部变量空间,其余的数据类型只占用1个4.局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法在帧中要分配多大的局部变量空间是完全确定的

3.本地方法栈

1.本地方法栈和虚拟机栈的区别在于,虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为Native放法服务2.有的虚拟机,如HotSpot,直接把本地方法栈和虚拟机栈合二为一

4.Java堆

1.Java堆是Java虚拟机所管理的内存中最大的一块2.Java堆在虚拟机启动时创建,被所有线程共享3.Java堆唯一的目的是存放对象实例,几乎所有的对象实例和数组都在这里4.Java堆为了便于更好的回收和分配内存,可以细分为:“新生代和老生代”,或者“Eden\ From Survivor\ To Survivor等”5.Java堆可以处在物理上不连续的内存空间中,只要逻辑上是连续的即可

5.方法区

1.方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据2.如何实现方法区属于虚拟机的实现细节,不受虚拟机规范约束3.方法区可以选择不实现垃圾回收4.方法区的内存回收目标主要针对常量池的回收和对类型的卸载

6.其他

1.运行时常量池用于存放编译器生成的各种字面量和符号引用2.直接内存不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域

虚拟机对象探秘

1.对象的创建

1.虚拟机在遇到一条new指令时,首先会检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查该类是否已加载。如果没有加载,还要先执行相应的类加载过程2.内存分配方式有两种,选择哪种方式由Java堆是否规整决定,若规整,则采用“指针碰撞”方式,若不规整,则采用“空闲列表方式”;而是否规整又由所采用的垃圾回收机制决定3.解决并发环境下的分配内存有两种方式:

  • 对分配内存空间的动作进行同步处理
  • 把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在堆中预先分配一小块内存(TLAB)

4.内存分配完成后还要将分配的内存空间都初始化为零值,从而保证对象的实例字段在Java代码中可以不赋初始值就直接使用5.初始化后还要给对象进行相关各设置,诸如哈希码、GC分代年龄信息等6.上述工作结束后,从虚拟机视角来看,一个新的对象已经产生了;但从Java程序的角度来看,对象的创建才刚刚开始————进行Java代码的初始化操作

2.对象的内存布局

1.对象在内存中存储的布局可以分为三块区域:对象头、实例数据、对齐填充2.对象头一搬包含两部分内容:对象的运行时数据和类型指针。如果对象是数组,则还有一个部分来记录数组的长度3.运行时数据指哈希码、GC分代年龄等信息,被设计成一个非固定的数据结构以便在极小的空间存储尽量多的信息,会根据对象的状态复用自己的存储空间4.类型指针是对象指向他的类元数据的指针,虚拟机通过这个指针确定对象是哪个类的实例5.实例数据是对象真正存储的有效信息,即程序中的字段内容6.实例数据默认的分配策略是按顺序,相同宽度的字段总是分配在一起7.对齐填充不是必然的,仅仅起着占位符的作用8.对象的大小必须是8字节的整数倍,对象头部分正好是8字节的倍数

3.对象的访问定位

1.Java程序通过栈上的reference数据来操作堆上的具体对象2.对象的访问形式取决于虚拟机,目前主流的有两种方式:使用句柄 和 直接指针3.使用句柄是间接访问,优点是reference中存储的是稳定的句柄地址,对象移动时只会改变句柄中的实例数据指针4.使用直接指针是直接访问,优点就是速度快

OutOfMemoryError异常

1.Java堆溢出

1.Java堆用于存储对象实例,只要不断的创建对象,且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量达到最大堆的容量限制之后就会产生内存溢出的异常

2.虚拟机和本地方法栈的溢出

1,HotSpot虚拟机中并不区分虚拟机栈和本地方法栈2.关于虚拟机栈和本地方法栈,Java虚拟机规范中描述了量中异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常3.在单个线程下,无论是由于栈太小还是虚拟机栈容量太小,当内存无法分配时,虚拟机抛出的都是StackOverflowError异常 3.方法区和运行时常量池溢出

2..在JDK 1.6中 intern() 方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用2.在JDK 1.7中 intern() 方法不会再复制实例,只是在常量池中记录首次出现的实例引用

3.StringBuilder 创建的字符串实例在Java堆上