程序计数器(-Program Counter Register线程私有)
是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。
虚拟机栈(VM Stack-线程私有)
Java虚拟机栈可能出现两种类型的异常:
- 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
- 虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。
本地方法栈(Native Method Stack-线程私有)
堆(Heap-线程共享)
方法区(Method Area-线程共享)
运行时常量池:是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用。
gc机制
1.判断对象是否存活
1.1引用计数算法
早期判断对象是否存活大多都是以这种算法,这种算法判断很简单,简单来说就是给对象添加一个引用计数器,每当对象被引用一次就加1,引用失效时就减1。当为0的时候就判断对象不会再被引用。
优点:实现简单效率高,被广泛使用与如python何游戏脚本语言上。
缺点:难以解决循环引用的问题,就是假如两个对象互相引用已经不会再被其它其它引用,导致一直不会为0就无法进行回收。
1.2可达性分析算法
目前主流的商用语言[如java、c#]采用的是可达性分析算法判断对象是否存活。这个算法有效解决了循环利用的弊端。
它的基本思路是通过一个称为“GC Roots”的对象为起始点,搜索所经过的路径称为引用链,当一个对象到GC Roots没有任何引用跟它连接则证明对象是不可用的。
2.垃圾回收算法
首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区);然后清空Eden区 和 ServivorFrom 区,最后将ServicorTo 区对象移到ServivorFrom区
3.垃圾收集器
年轻代收集器
Serial、ParNew、Parallel Scavenge
3.1Serial 垃圾收集器(单线程、复制算法)
在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束,适用于单核CPU,java 虚拟机运行在 Client 模式下默认的新生代垃圾收集器。
3.2ParNew 垃圾收集器(Serial+多线程)
除了是多线程其它和serial一样,默认开启线程数和当前cpu数量相同,java虚拟机运行在 Server 模式下新生代的默认垃圾收集器。
3.3Parallel Scavenge 收集器(多线程复制算法、高效)
采用复制算法的收集器,和ParNew一样支持多线程,采用自适应调节策略,重点关心的是吞吐量【吞吐量 = 代码运行时间 / (代码运行时间 + 垃圾收集时间) 如果代码运行100min垃圾收集1min,则为99%】
老年代收集器
Serial Old、Parallel Old、CMS收集器
在 Server 模式下,主要有两个用途:
1. 在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用。
3.5Parallel Old 收集器(多线程标记整理算法)
在jdk6以前,新生代的Parallel Scavenge只能和Serial Old配合使用【根据图,没有这个的话只剩Serial Old,而Parallel Scavenge又不能和CMS配合使用】,而且Serial Old为单线程Server模式下会拖后腿【多核cpu下无法充分利用】,这种结合并不能让应用的吞吐量最大化。 Parallel Old的出现结合Parallel Scavenge,真正的形成“吞吐量优先”的收集器组合 。
以一种获取最短回收停顿时间为目标的收集器。【重视响应,可以带来好的用户体验,被sun称为并发低停顿收集器
分为4个阶段
1.初始标记:标记一下GC Roots能直接关联到的对象,速度很快,需要暂停工作线程
2.并发标记:进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。
3.重新标记:为了修正因并发标记期间用户程序运作而产生变动的那一部分对象的标记记录,会有些许停顿,时间上一般 初始标记 < 重新标记 < 并发标记,需要暂停工作线程
4.并发清除:清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。
新型收集器
G1收集器
3.7G1收集器
1. 基于标记-整理算法,不产生内存碎片。