当java文件编译成字节码文件后就会通过JVM的类装载子系统装载近运行时数据区(内存模型),这个过程会发生以下的事情:
- 当你new一个对象时,该对象就会被放到内存模型的堆中
- 每一个线程程都会被分配一块栈空间
-
线程中的每一个方法都会有一个栈帧,而且由于栈是FILO(frist in last out,先进后出),所以后执行的方法所有栈帧就会先出栈,也就是销毁。
image.png
堆
二、垃圾回收机制(GC)
-
首先需要明确一个概念,什么叫GC-root?
1.在JVM的里面单独的线程
2.元空间中的静态变量
3.元空间中的常量对象
4.本地方法栈的引用对象
也就是可以以GCRoot对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象,这也就是可达性分析算法。
当GCroot对象出栈也就是销毁后,他下面的引用节点也就成为了不可达对象,也就会成为垃圾对象。
可达性分析算法是GC算法中非常典型的算法。 -
需要说明的是,堆内存是GC重点回收区域
1.我们都知道在Java虚拟机中进行垃圾回收的场所有两个,一个是堆,一个是方法区。在堆中存储了Java程序运行时的所有对象信息,而垃圾回收其实就是对那些“死亡的”对象进行其所侵占的内存的释放,让后续对象再能分配到内存,从而完成程序运行的需要。关于何种对象为死亡对象,在下一部分将做详细介绍。Java虚拟机将堆内存进行了“分块处理”,从广义上讲,在堆中进行垃圾回收分为新生代(Young Generation)和老生代(Old Generation);从细微之处来看,为了提高Java虚拟机进行垃圾回收的效率,又将新生代分成了三个独立的区域(这里的独立区域只是一个相对的概念,并不是说分成三个区域以后就不再互相联合工作了),分别为:Eden区(Eden Region)、From Survivor区(Form Survivor Region)以及To Survivor(To Survivor Region),而Eden区分配的内存较大,其他两个区较小,每次使用Eden和其中一块Survivor。Java虚拟机在进行垃圾回收时,将Eden和Survivor中还存活着的对象进行一次性地复制到另一块Survivor空间上,直到其两个区域中对象被回收完成,当Survivor空间不够用时,需要依赖其他老年代的内存进行分配担保。当另外一块Survivor中没有足够的空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老生代,在老生代中不仅存放着这一种类型的对象,还存放着大对象(需要很多连续的内存的对象),当Java程序运行时,如果遇到大对象将会被直接存放到老生代中,长期存活的对象也会直接进入老年代。如果老生代的空间也被占满,当来自新生代的对象再次请求进入老生代时就会报OutOfMemory异常。新生代中的垃圾回收频率高,且回收的速度也较快。就GC回收机制而言,JVM内存模型中的方法区更被人们倾向的称为永久代(Perm Generation),保存在永久代中的对象一般不会被回收。其永久代进行垃圾回收的频率就较低,速度也较慢。永久代的垃圾收集主要回收废弃常量和无用类。以String常量abc为例,当我们声明了此常量,那么它就会被放到运行时常量池中,如果在常量池中没有任何对象对abc进行引用,那么abc这个常量就算是废弃常量而被回收;判断一个类是否“无用”,则需同时满足三个条件:(1)、该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例; (2)、加载该类的ClassLoader已经被回收 (3)、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的是可以回收而不是必然回收。
大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC;同理,当老年代中没有足够的内存空间来存放对象时,虚拟机会发起一次Major GC/Full GC。只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full CG,如FullGC完毕后老年区还是满状态,这种情况就会发生OOM(内存分配完了)
-
下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代已经被移除。
image.png
网友评论