GC垃圾回收不会涉及到虚拟机栈的
GC垃圾回收的几种基础算法
先言-将内存空间比喻为一个excel表格,每个单元格为存储的数据。
标记清除法:遍历,将非垃圾存储单元格进行标记,再将所有未标记的对象当做垃圾清除。这将带来内存碎片问题(因为存活对象的位置变得杂乱无章)
复制:将excel表一份为二,只用其一,在进行垃圾清扫时,将存活对象放到另一个侧,从新排列。这样就解决了会出现内存碎片问题。
标记整理法:前期同标记清除法一样去除垃圾,而后将存活对象向一端进行移动整理。
GC是如何判断垃圾的
判断对象是否为垃圾通常有两种方式
1.引用计数法 给对象一个引用计数器,用到它则+1,失效时-1,值为0即代表其为垃圾。
2.可达性分析法 想像一棵以GC Roots对象为根节点的树,子节点为其包含关联的对象。凡未在树中的对象,即为不可能被引用的对象,即垃圾。
JVM中,可作为GC Roots的有
虚拟机栈中引用的对象
本地方法栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
java采用的是可达性分析法来判断垃圾。
Java内存分配和回收的机制
分代分配和回收,它综合了GC基础算法中的标记清除法和复制法。
它根据对象的存活时间,将堆分为:新生代、老年代、永久代 三个区域
新生代 它的整体区域远小于老年代区域,它存储存活时间不久的对象。它包含Eden、Survice0、Survice1 三个区域,对象被新建时,存放在Eden区,在Eden区满了之后,会触发Minor GC执行垃圾回收,将垃圾清除并将存活对象放到一个Service区,下一次Eden区满了,会将非空的Service区数据推到老年代中,然后将Eden中垃圾清除并将Eden中的存活对象推到另一个Service区(两个Service区总有一个是空的),新生代就这样一直循环往复。新生代采用的是==“停止-复制”清理法==。它基于的GC基础算法是复制法,只不过Eden与Survice的比例并不是1:1而是8:1。因为实际当中,存活的对象是非常少的。
老年代 它存储那些存活时间久的对象,当它满了之后会触发full GC执行垃圾回收,它采用的是GC基础算法中的标记整理法或标记清除法(视所用具体的垃圾收集器而定),毕竟既已成为老年代数据,即说明其应该继续存活,垃圾为极少数。一些特别大的对象会直接跳过新生代进入老年代,因为新生代区域太小了,老年代的区域很大。
垃圾收集器可以分为两类:新生代垃圾收集器、老年代垃圾收集器
JAVA在新生代垃圾回收时,会有“停止”这一操作,也就是说需要暂停其他所有线程。
基本各种类型的新生代垃圾收集器都会有“停止”这个现象,大家都在尽力使“停止"的时间变短。
JAVA对象的访问
Object obj = new Object();
第一个Object其实是指向方法区的,它代表着类的定义信息。
obj引用,存在虚拟机栈中的局部变量表中。
实例对象的生成需要对象具体数据和对象的元数据(类的定义信息)。
访问具体对象的方式有两种
1.通过句柄访问 在堆中专门开僻一个区域"句柄池",池中的数据包含到"实例池"和"元数据"的指针。
2.通过指针直接访问 obj引用即存放的实例池的指针,可以直接指向实例池的数据,实例池中的数据再包含元数据池的指针。
如何解决OOM(内存溢出)
先判断是内存泄漏还是普通的内存溢出
内存泄漏是指一个不再被程序使用的对象或变量还在内存中占有存储空间。
如果是内存泄漏,我们要通过工具查看泄漏对象与GC Root对象的链关系进行解决。
如果是普通的内存溢出,我们要检查堆大小的参数配置及方法区(元数据区)大小的参数配置。