JVM java的内存模型和GC算法

152 阅读4分钟

你是否有喜欢的明星,喜欢的偶像,一个人喜欢某个人,那这个人身上一定有他所欣赏的东西,我喜欢五月天,五月天有一颗少年心,并且他的歌词总能唱到我的心坎里,世界那么大,如果拥有一个和你相同想法的人,那么就不会那么孤单,就算他不认识你。所以,如果一颗心感到冰冷,那么可能是你没有找到一个可以温暖你内心的人,我相信只要永不止步,总有一天你能找到你所热爱的人,你所热爱的事。

啰嗦完了,今天开始说说java的内存模型

1 程序计数器

2 本地方法栈

3 java虚拟机栈

4 堆

5 元空间

其实就是这五块区域

具体介绍一下

1 程序计数器 记录每一步字节码的行号 ,线程私有,保证多个线程过来执行顺序不错乱

2 本地方法栈 用为native方法执行时占用的空间,就是JNI方法的调用,但是如果大量使用,也就是丧失了java的跨平台性

3 java 虚拟机栈

虚拟机栈又细分为

   1)局部变量表
   
   2)操作栈

   3)动态连接

   4) 方法返回地址

我们平常说的栈的结构其实指的是操作栈的结构,他结构就像一个枪的弹夹,最后压入的子弹最先发射(弹夹放子弹是从弹夹的头部放入),那这几个都干嘛的

局部变量表会存储方法的参数和局部变量,向它里面放数据的字节码指令是istore

通过代码来理解一下

public int stack() {
    int x = 10;
    int y = 11;
    int z = x + y;
    return z;

}

他的字节码是

public int stack();
Code:
0: bipush        10     //将10压入操作栈
2: istore_1             //并将10保存到局部变量表istore_1中
3: bipush        11     //将11压入操作栈
5: istore_2                          //并将11保存到局部变量表istore_2中
6: iload_1                           //将局部变脸表1压入操作栈
7: iload_2                           //将局部变脸表2压入操作栈
8: iadd                              //把栈中值取出来放到cpu中计算,并压回栈顶
9: istore_3                          //把栈顶的值弹出来放到局部表量表3
10: iload_3                           //局部表量表3压入操作栈
11: ireturn                          //返回栈顶元素值
@return

通过这个执行过程,就能够透彻的了解了,方法的执行其实就是压栈和出栈的过程,同时也注意到,里面出现了局部变量表1,2,3,其实局部变量表也有结构,他的结构就像一个抽屉。

动态链接是每个方法在常量池中的动态引用,而方法返回地址就是执行ireturn,返回命令的地方

4 堆 heap

堆是jvm里面的重要人物,从OOM就可以看出来,他主要就是存储对象,他的结构是啥呢,

它分为新生代区,老年代区,新生代又分为Edgn区,和Survivor区,Survivor又分为s0和s1区

那他们是如果工作的

创建对象的时候,会给对象分配内存,这时候如果对象大于Edgn区的存储空间,那么就放到老年代区,如果不大于就放到Edgn区,如果Edgn区满了,就会触发youngGc,没有被引用就会方法Survivor区,那放到他的哪块空间呢,会放到没有使用的s空间,然后把使用的那块空间清楚,之后交换位置,如果ygc后占用的空间大于Survivor区的最大空间,那么就放到老年代,那互相交换的那部分会一直交换吗?不回,有一个最大次数,15次,如果交换次数大于15次,那就把Survivor区的存储放到老年代区,如果老年代放不下了,那就会触发FullGC,如果FullGc后老年代还放不下,那就OOM了。

5 元空间

jdk7到8有一个变化,就是移除了永久代,他是直接分配到内存的,包含类原信息,静态字段等。

说完jvm的内存结构再说说GC算法

描述堆内结构的时候说了YGC与FullGC,那他是怎么GC的?关键就是判断一个对象有没有用,每个对象都有一个引用,我们只需要判断这个对象有没有引用就可以了,现在主流的使用使用可达性分析法,以前有使用引用计数法,关于这一部分比较复杂,下次再整理