java-垃圾回收相关

  • Post author:
  • Post category:java


参考: https://blog.csdn.net/xxxx3/article/details/81009524

https://mp.weixin.qq.com/s/yz631xKPv-RUUE9t4xuWPQ

https://blog.csdn.net/justloveyou_/article/details/71216049

java垃圾回收,就是指jvm内存回收机制,这就要说到内存溢出了

jvm运行时数据区:分为5个部分,分别是 方法区,堆,栈,本地方法栈,程序计数器,

其中程序计数器是唯一一个不会内存溢出的地方,另外方法区和堆是所有线程共享的,其余三个是线程独有的

能够发生内存溢出(OutOfMemoryError)的地方:堆,栈,方法区,


通常栈的内存溢出叫

: java.lang.StackOverflowError 原因: 线程请求的栈深度超过虚拟机所允许的最大深度


堆的内存溢出是

: java.lang.OutOfMemoryError: Java heap space 原因: 堆内存空间满了,不能创建新的对象


方法区溢出:

OutOfMemoryError: PermGen space 原因: 虚拟机加载了大量的jar或class,导致方法区空间不足

还有一种线程错误: OutOfMemoryError:unable to create new native thread 可能原因(1)在程序里面使用了http-client,http-client 有两个超时参数,但实际只设置了一个参数,然后当对方的服务器宕机之后,httpClient一直处于等待状态,导致线程堆积。

(2)做了多线程,而每个线程的耗时过多导致的(新增线程的速度比处理完成线程的速度快)

JVM垃圾回收的场所:

虚拟机栈溢出(本地方法栈):

与线程栈相关的内存异常有两个:

a)StackOverflowError(方法调用层次太深,内存不够新建栈帧)

b)OutOfMemoryError(线程太多,内存不够新建线程)

堆(重点讲):

java.lang.OutOfMemoryError: Java heap space

导致堆溢出的可能有,

1,死循环,内存无法释放

2,创建太多对象

堆是JVM内存占用最大,管理最复杂的一个区域。其唯一的用途就是存放对象实例:所有的对象实例及数组都在对上进行分配。

jdk1.8后,字符串常量池从永久代中剥离出来,存放在其中。

存储的是我们new来的对象,不存放基本类型和对象引用。

由于创建了大量的对象,垃圾回收器主要工作在这块区域。

https://s4.51cto.com/images/blog/201808/21/b116170771ecb3117ae7fead03fcaa0d.png

jvm内存分为:堆和非堆(方法区)

堆内存分为年轻代(Young Generation)和年老代(Old Generation)

年轻代又分为生成区(Eden)和幸存区(Survivor)

幸存区由FromSpace和ToSpace组成

年轻代,FromSpace,ToSpace比例默认是8:1:1

Class文件中的信息被字节码执行引擎加载到了方法区,从而形成了运行时常量池。

方法区(重点讲):

方法区只是一个概念,可以由永久代(JDK1.7)或元空间(JDK1.8)实现,

JDK1.8的元空间并不在JVM中而是使用本地内存(有了元空间,方法区就不会OOM了)

元空间有俩参数,最大初始化内存和最大内存,

JVM是如何进行垃圾回收的:

当需要创建一个对象的时候,会去运行时常量池寻找该类的,如果没有被加载,就会先加载该类的信息,

然后生成新的对象首先放到年轻代(Eden),当Eden满了之后,就回触发Minor GC,存活下来的对象会被移动到幸存区(Survivor)(S0)

当S0满了会触发,会触发Minor

GC.对S0进行垃圾回收,存活的对象移到S1,S0被清空,经历一定次数(默认15)的Minor GC,会被移动到老年代(Old Generation)

老年代存储长期存活的对象,当老年代被占满的时候,会触发Major GC(等于Full GC) (关于Major GC和Full GC可以参考: https://blog.csdn.net/g7n3f/article/details/50829503)

给大对象分配内存的时候,Eden区已经没有足够的内存空间了,大对象就会直接进入老年代

Stop The World: Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)

所有的收集器都会STW,但是full gc(Major)的时间远远大于minor gc的时间

为了避免卡顿,就要尽量减少Major的发生

Full GC会清理整个内存空间针对整个新生代、老生代、方法区

Full GC触发条件:

(1)调用System.gc时,系统建议执行Full GC,但是不必然执行

(2)老年代空间不足

(3)方法去空间不足

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

垃圾回收器: 参考: https://blog.51cto.com/lizhenliang/2164876

串行收集器(Serial): 比较老的收集器,单线程。收集时,必须暂停应用的工作线程,直到收集结束。 (复制算法)

并行收集器(Parallel): 多条垃圾收集线程并行工作,在多核CPU下效率更高,应用线程仍然处于等待状态。(复制)

Serial Old :老年代 标记整理

CMS收集器(Concurrent Mark Sweep):CMS收集器是缩短暂停应用时间为目标而设计的,是基于标记-清除算法实现

Parallel Old: 老年代,标记整理

G1收集器(Garbage First):全部,复制标记整理

垃圾回收算法:

标记-清除(Mark-Sweep):

GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。

同时会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。

复制(Copy):

将内存按容量划分为两块,每次只使用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。

这样使得每次都是对半个内存区回收,也不用考虑内存碎片问题,简单高效。缺点需要两倍的内存空间。

标记-整理(Mark-Compact):

也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。

此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。



版权声明:本文为xy3233原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。