内存的申请和释放
我们在编程的过程中,经常需要为了存放数据而申请内存,当数据使用结束之后需要释放内存,各种高级编程语言都提供了申请和释放内存的方法:
- c: new - free
- c++:new-delete
- java:new-垃圾自动回收
对于c/c++来说,需要手动申请和释放内存,坏处是:一、忘记释放内存,生成过多内存垃圾,导致内存泄漏,二、重复释放内存,程序运行中报错。 因此,在java中,使用垃圾自动回收器自动回收内存,可以减少编程中出现的错误。怎么去设计一个垃圾回收器,首先要考虑以下问题。
什么是垃圾?
内存垃圾是指在内存中没有任何引用指向的对象。
如何找到垃圾?
一、引用计数法。
当一个对象被一个引用指向之后,他的引用计数+1,当一个对象的引用计数变为0,说明没有引用指向它,它在内存中是不可被找到的,可以视为内存垃圾,可以回收。缺点是,当内存中出现几个对象循环指向,每个对象的引用计数都为1,但是,这些对象都是不可达的,都属于内存垃圾。
二、可达性分析。
JVM将某些对象视为根对象,根对象中的成员变量会指向其他的对象,因此,层层指向会形成一颗对象树,树中的对象被认为是可以达的,树外的对象视为不可达。当JVM遇到不可达对象时,会将对象进行一次标记,如果标记后第二次任然不可达,则对象被当成垃圾清除。
关于为什么需要进行2次标记?
某些对象中会有一个finalize()方法,这个finalize函数的意义同finalize关键字意义类似。如果JVM发现对象实现了finalize()函数,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环,将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。
什么样的对象可以作为根对象?
- 虚拟机栈中的引用对象(方法的本地变量)
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象(声明为final的常量值)
- 本地方法栈中JNI的引用对象(方法的本地变量)
怎么清理垃圾?
一、标记-清除算法 mark-sweep
将内存中的垃圾对象直接标记然后清除,这样缺点是会导致过多的内存碎片,降低系统的效率。
二、复制-清除算法 copy-sweep
这种算法将内存一分为二,每次使用内存的一半,当一半内存满了之后,进行一次标记,将存活的对象复制到另一半内存,对原来的一半内存直接清除。这种算法不会产生内存碎片,效率高,但是每次只使用一半内存,所以内存的利用率只有50%,利用率低。
三、标记-压缩算法 mark-compact
这种算法进行一次标记之后,将所有存活的对象移动到内存的一边,剩下的区域直接清理。这种算法利用率高、不产生碎片,但是需要频繁移动存活对象,所以效率低。
JVM分代算法
堆逻辑分区
JVM根据内存的活跃度将堆内存划分为年轻代(young)和年老代(old),内存大小的比为1:3。在年轻代中,堆内存被划分为Eden、survivor1和survivor2区,内存大小为8:1:1。(内存大小与比例可以设置)
当Java创建一个对象时,首先会在Eden中分配一块内存。如果整个Eden区都装不下,则直接在年老代中创建对象。如果Eden能能装下但是空闲内存不够,则会进行一次Minor GC\Young GC\YGC。在年轻代中使用的是复制-清除算法(因为80%的对象是需要清除的,能持续很久的对象很少,所以只需要找出少量的存活对象然后复制,节省时间,这也是Eden:s1:s2=8:1:1的原因),清理过程如下:
- 标记Eden和s1(survivor1)中的对象,将存活对象移动到s2(survivor2),清除Eden和s1中的所有对象
- 下一次回收时,标记Eden和s2中的对象,将存活对象移动到s1,清除Eden和s2中的所有对象
- 重复1、2步骤
- 当一个对象的年龄足够(>15,cms>16)时,直接将对象移动到老年代。
当老年区内存不够时,触发Major GC\Full GC\FGC,Major GC采用的是标记整理法,对这个内存进行标记整理,当整理时程序中的其他线程需要挂起,所以会进入STW(stop the world)状态,产生停顿,影响程序的正常运行。所以垃圾回收的调优就是尽量减少FGC的次数。
现有的垃圾回收器
年轻代垃圾回收器
Serial 单线程,串行回收,垃圾回收时其他线程将挂起
Parallel Scavange 并行回收,多个线程进行垃圾回收,但是主程序的线程也需要挂起
Parallel New Parallel Scavenge的升级版,为了配合老年代回收器CMS回收器
老年代垃圾回收器
Serial old 单线程
Parallel old 多线程
CMS(ConcurrentMarkSweep) 高效率老年代垃圾回收算法,STW时间短
其他垃圾回收器
G1、ZGC、Shenandoah、Eplison。
JVM命令行分类
标准命令 -:所有hotspot虚拟机都支持
特定命令 -x:特定的hotspot版本支持
不稳定命令 -XX:下个版本可能取消