@ -345,6 +345,24 @@ JVM引入动态年龄计算, 主要基于如下两点考虑:
4. 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
4. 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
5. 由Eden区、From Space区向To Space区复制时, 对象大小大于To Space可用内存, 则把该对象转存到老年代, 且老年代的可用内存小于该对象大小
5. 由Eden区、From Space区向To Space区复制时, 对象大小大于To Space可用内存, 则把该对象转存到老年代, 且老年代的可用内存小于该对象大小
### TLAB
在Java中, 典型的对象不再堆上分配的情况有两种: TLAB和栈上分配( 通过逃逸分析) 。JVM在内存新生代Eden Space中开辟了一小块线程私有的区域, 称作TLAB( Thread-local allocation buffer) 。默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢, 它们不存在线程共享也适合被快速GC, 所以对于小对象通常JVM会优先分配在TLAB上, 并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。也就是说, Java中每个线程都会有自己的缓冲区称作TLAB( Thread-local allocation buffer) , 每个TLAB都只有一个线程可以操作, TLAB结合bump-the-pointer技术可以实现快速的对象分配, 而不需要任何的锁进行同步, 也就是说, 在对象分配的时候不用锁住整个堆, 而只需要在自己的缓冲区分配即可。
### Java对象分配的过程
1. 编译器通过逃逸分析, 确定对象是在栈上分配还是在堆上分配。如果是在堆上分配, 则进入2.
2. 如果tlab_top + size < = tlab_end, 则在在TLAB上直接分配对象并增加tlab_top 的值, 如果现有的TLAB不足以存放当前对象则3.
3. 重新申请一个TLAB, 并再次尝试存放当前对象。如果放不下, 则4。
4. 在Eden区加锁( 这个区是多线程共享的) , 如果eden_top + size < = eden_end则将对象存放在Eden区, 增加eden_top 的值, 如果Eden区不足以存放, 则5。
5. 执行一次Young GC( minor collection)
6. 经过Young GC之后, 如果Eden区任然不足以存放当前对象, 则直接分配到老年代。
### 对象内存分配的两种方法
1. 指针碰撞(Serial、ParNew等带Compact过程的收集器) : 假设Java堆中内存是绝对规整的, 所有用过的内存都放在一边, 空闲的内存放在另一边, 中间放着一个指针作为分界点的指示器, 那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离, 这种分配方式称为“指针碰撞”( Bump the Pointer) 。
2. 空闲列表(CMS这种基于Mark-Sweep算法的收集器) : 如果Java堆中的内存并不是规整的, 已使用的内存和空闲的内存相互交错, 那就没有办法简单地进行指针碰撞了, 虚拟机就必须维护一个列表, 记录上哪些内存块是可用的, 在分配的时候从列表中找到一块足够大的空间划分给对象实例, 并更新列表上的记录, 这种分配方式称为“空闲列表”( Free List) 。
### JVM类加载过程
### JVM类加载过程
类从被加载到虚拟机内存中开始, 到卸载出内存为止, 它的整个生命周期包括: 加载、验证、准备、解析、初始化、使用和卸载7个阶段。
类从被加载到虚拟机内存中开始, 到卸载出内存为止, 它的整个生命周期包括: 加载、验证、准备、解析、初始化、使用和卸载7个阶段。