首先了解一下JVM
jvm分为栈区和堆区:
- 栈区主要存放,基本类型的变量数据和对象的引用。对象本身存放在堆区或者常量池中(字符串常量存放在常量池中)
- 堆区主要存放:所有new出来的对象
- 静态域:存放静态成员(static定义的)
- 常量池:存放字符串常量和基本类型常量(加了final)
*注意字符串如果是new出来的存放在堆区,如果编译期就创建好了(用""创建)才放到常量池中。
栈区的数据大小和生命周期是可以确定的,没用了会被清除,不涉及到gc。
堆区的对象涉及到GC,所以gc主要发生在堆区。
例如一个方法中创建了一个巨大对象,在方法中手动调用gc,因为这个方法还持有引用,内存不会回收这个对象。如果这个方法执行完了再回收这个对象,这个对象就会被回收。如果中间给这个值设置=null,这个原有对象的内存会被回收,因为栈引用对象发生了变化,不在引用原来内存区域,这块内存就会被回收。

20210430112143.jpg
gc一般回收
- 没有根对象持有的对象
- 弱引用和虚引用对象
- 标记过的软引用对象
再来说下四种引用类型
- 强引用一般是不会被回收的
- 软引用描述是可能还有用,但非必要对象,内存不够的时候会第二次gc回收
- 弱引用只要gc了就会被回收
- 虚引用基本只要一创建就会被回收,我们使用意义不大
接下来我们说一下GC算法
核心是从root对象开始扫描对象
-
标记-清理
先从上到下从左到右扫描整个内存块,将无用的标记,然后清除掉
缺点:回收后的内存不连贯,造成大量内存碎片;回收内存多时效果差。 -
标记-整理
先从上到下从左到右扫描整个内存块,将无用的内存标记,然后将无用的都移动到一端,有用的移动到另一端,最后直接将无用的清除掉。
优点:避免了内存碎片
缺点:效率低,回收越多越慢 -
复制算法
将内存分为大小相同的AB区域,每次使用一块区域,区域满了对这个区域进行扫描,将存活的转移到另外一个区域,剩下的全部清除
优点:快,不用遍历回收
缺点:浪费内存
针对三种算发的优缺点,我们设计了分代算法
分代算法分
-
新生代(3复制算法)
也可以分成伊甸园,S0,S1三个区域(8:1:1)
新创建的对象会放到伊甸园区,等Eden区满了进行一次GC,将存活的对象复制到S0。
多次重复GC后Eden的存活对象吧S0区域填满了后S0进行一次GC,还是利用复制算法将S0区域的存货对象复制到S1中,S1和S0交替使用,每次交替年拉+1,当年龄到了15后就可以转入老年代了。 -
老年代(2标记整理)
进入老年代的对象有两种,一种是创建对象S0和S1切换存活了15次的,另外一种是新生代gc后还是内存不够的直接放老年代。(复制算法需要内存担保,老年代可以做内存担保,8:1:1如果新生代创建的对象gc一次存活超过10%的时候,可以直接把对象放到老年代)
老年代采用标记整理算法,因为老年代的需要回收对象很少了。
- 当老年代还是放不下对象的时候 ,发生OOM。