『编程』12000字 史上最最最完整深入解析JVM,请先收藏再看!( 四 )


6、几种不同的垃圾回收类型:(1)Minor GC:从年轻代(包括Eden、Survivor区)回收内存 。
A、当JVM无法为一个新的对象分配内存的时候 , 越容易触发Minor GC 。 所以分配率越高 , 内存越来越少 , 越频繁执行Minor GC
B、执行Minor GC操作的时候 , 不会影响到永久代(Tenured) 。 从永久代到年轻代的引用 , 被当成GC Roots , 从年轻代到老年代的引用在标记阶段直接被忽略掉 。

(2)Major GC:清理整个老年代 , 当eden区内存不足时触发 。 (3)Full GC:清理整个堆空间 , 包括年轻代和老年代 。 当老年代内存不足时触发
HotSpot 虚拟机详解:1、 Java对象创建过程:(1)虚拟机遇到一条new指令时 , 首先检查这个指令的参数能否在常量池中定位到一个类的符号引用 , 并检查这个符号引用代表的类是否已经加载、连接和初始化 。 如果没有 , 就执行该类的加载过程 。 (2)为该对象分配内存 。 A、假设Java堆是规整的 , 所有用过的内存放在一边 , 空闲的内存放在另外一边 , 中间放着一个指针作为分界点的指示器 。 那分配内存只是把指针向空闲空间那边挪动与对象大小相等的距离 , 这种分配称为“指针碰撞”B、假设Java堆不是规整的 , 用过的内存和空闲的内存相互交错 , 那就没办法进行“指针碰撞” 。 虚拟机通过维护一个列表 , 记录哪些内存块是可用的 , 在分配的时候找出一块足够大的空间分配给对象实例 , 并更新表上的记录 。 这种分配方式称为“空闲列表“ 。 C、使用哪种分配方式由Java堆是否规整决定 。 Java堆是否规整由所采用的垃圾收集器是否带有压缩整理功能决定 。 D、分配对象保证线程安全的做法:虚拟机使用CAS失败重试的方式保证更新操作的原子性 。 (实际上还有另外一种方案:每个线程在Java堆中预先分配一小块内存 , 称为本地线程分配缓冲 , TLAB 。 哪个线程要分配内存 , 就在哪个线程的TLAB上分配 , 只有TLAB用完并分配新的TLAB时 , 才进行同步锁定 。 虚拟机是否使用TLAB , 由-XX:+/-UseTLAB参数决定)(3)虚拟机为分配的内存空间初始化为零值(默认值)(4)虚拟机对对象进行必要的设置 , 例如这个对象是哪个类的实例、如何才能找到对象的元数据信息、对象的Hash码、对象的GC分代年龄等信息 。 这些信息存放在对象的对象头中 。 (5) 执行<init>方法 , 把对象按照程序员的意愿进行初始化 。
2、 对象的定位访问的方式(通过引用如何去定位到堆上的具体对象的位置):(1)句柄:使用句柄的方式 , Java堆中将会划分出一块内存作为作为句柄池 , 引用中存储的就是对象的句柄的地址 。 而句柄中包含了对象实例数据和对象类型数据的地址 。
(2)直接指针:使用直接指针的方式 , 引用中存储的就是对象的地址 。 Java堆对象的布局必须必须考虑如何去访问对象类型数据 。
(3)两种方式各有优点:A、使用句柄访问的好处是引用中存放的是稳定的句柄地址 , 当对象被移动(比如说垃圾回收时移动对象) , 只会改变句柄中实例数据指针 , 而引用本身不会被修改 。 B、使用直接指针 , 节省了一次指针定位的时间开销 。
3、HotSpot的GC算法实现:(1)HotSpot怎么快速找到GC Root?HotSpot使用一组称为OopMap的数据结构 。 在类加载完成的时候 , HotSpot就把对象内什么偏移量上是什么类型的数据计算出来 , 在JIT编译过程中 , 也会在栈和寄存器中哪些位置是引用 。 这样子 , 在GC扫描的时候 , 就可以直接知道哪些是可达对象了 。 (2)安全点:A、HotSpot只在特定的位置生成OopMap , 这些位置称为安全点 。 B、程序执行过程中并非所有地方都可以停下来开始GC , 只有在到达安全点是才可以暂停 。 C、安全点的选定基本上以“是否具有让程序长时间执行“的特征选定的 。 比如说方法调用、循环跳转、异常跳转等 。 具有这些功能的指令才会产生Safepoint 。 (3)中断方式: