jvm内存模型与gc

程序计数器(-Program Counter Register线程私有) 

是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。

虚拟机栈(VM Stack-线程私有)

是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)
用于存储局部变量表、操作数栈、动态链接、方法出口等信息 每一个方法从调用直至执行完成
的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

Java虚拟机栈可能出现两种类型的异常:

  1. 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
  2. 虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。

本地方法栈(Native Method Stack-线程私有)

是描述本地方法执行的内存模型,与虚拟机栈类似。

堆(Heap-线程共享)

运行时数据区 ,是被线程共享的一块内存区域, 创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行 垃圾收集的最重要的内存区域 堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误(OutOfMemoryError)。
细分为: 新生代 ( Eden 区 From Survivor 区 To Survivor 区 )和 老年代。

方法区(Method Area-线程共享)

用于 存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。

运行时常量池:是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用。

gc机制

1.判断对象是否存活

1.1引用计数算法
早期判断对象是否存活大多都是以这种算法,这种算法判断很简单,简单来说就是给对象添加一个引用计数器,每当对象被引用一次就加1,引用失效时就减1。当为0的时候就判断对象不会再被引用。
优点:实现简单效率高,被广泛使用与如python何游戏脚本语言上。
缺点:难以解决循环引用的问题,就是假如两个对象互相引用已经不会再被其它其它引用,导致一直不会为0就无法进行回收。

1.2可达性分析算法
目前主流的商用语言[如java、c#]采用的是可达性分析算法判断对象是否存活。这个算法有效解决了循环利用的弊端。
它的基本思路是通过一个称为“GC Roots”的对象为起始点,搜索所经过的路径称为引用链,当一个对象到GC Roots没有任何引用跟它连接则证明对象是不可用的。

不可达对象变为可回收对象至少要经过两次标记过程 。两次标记后仍然是可回收对象,则将面临回收。

2.垃圾回收算法

2.1标记清除算法
最基础的垃圾回收算法,分为 两个阶段,标注和清除 。标记阶段标记出所有需要回收的对象,清
除阶段回收被标记的对象所占用的空间。 最大的问题是内存碎片化严重 ,后续可能发生大对象不能找到可利用空间的问题。
2.2复制算法
 
按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉。优点是实现简单,内存效率高, 解决了碎片化问题,但是 可用内存被压缩到了原本的一半 。且存活对象增多的话,Copying 算法的效率会大大降低。
​​​​​​​
2.3标记整理算法
标记阶段和 Mark-Sweep 算法相同, 标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象
分代收集算法:对于不同的内存区域采取不同的垃圾回收算法
新生代(存活率低):复制算法
将内存分为一块Eden空间和From Survivor、To Survivor【保留空间】,三者默认比例为8:1:1,优先使用Eden区,若Eden区满,则将对象复制到From Survivor区。

首先,把 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收集器

3.4Serial Old 收集器(单线程标记整理算法 )
单线程的收集器,使用标记-整理算法, 这个收集器也主要是 运行在 Client 默认的 java 虚拟机默认的年老代垃圾收集器

在 Server 模式下,主要有两个用途:

1. 在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用。

2. 作为年老代中使用 CMS 收集器的后备垃圾收集方案。

3.5Parallel Old 收集器(多线程标记整理算法)

Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多线程的标记-整理算法,在 JDK1.6
才开始提供, 老年代的收集器大都采用此算法
在jdk6以前,新生代的Parallel Scavenge只能和Serial Old配合使用【根据图,没有这个的话只剩Serial Old,而Parallel Scavenge又不能和CMS配合使用】,而且Serial Old为单线程Server模式下会拖后腿【多核cpu下无法充分利用】,这种结合并不能让应用的吞吐量最大化。
Parallel Old的出现结合Parallel Scavenge,真正的形成“吞吐量优先”的收集器组合
3.6CMS 收集器(多线程标记清除算法)
 

以一种获取最短回收停顿时间为目标的收集器。【重视响应,可以带来好的用户体验,被sun称为并发低停顿收集器

分为4个阶段

1.初始标记:标记一下GC Roots能直接关联到的对象,速度很快,需要暂停工作线程

2.并发标记:进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

3.重新标记:为了修正因并发标记期间用户程序运作而产生变动的那一部分对象的标记记录,会有些许停顿,时间上一般 初始标记 < 重新标记 < 并发标记,需要暂停工作线程
4.并发清除:清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。

新型收集器

G1收集器

3.7G1收集器

1. 基于标记-整理算法,不产生内存碎片。

2. 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。
G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域 ,并且跟踪这些区域
的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间, 优先回收垃圾
最多的区域 。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收
集效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值