jvm内存模型

在这里插入图片描述

一、内存模型

1. 程序计数器
1)每个线程拥有一个程序计数器,在线程创建时创建,指向下一条指令的地址,执行本地native方法时,其值为undefined

2)Java是支持多线程的,程序先去执行A线程,执行到一半,然后就去执行B线程,然后又跑回来接着执行A线程,那程序是怎么记住A线程已经执行到哪里了呢?这就需要程序计数器了。因此,为了线程切换后能够恢复到正确的执行位置,每条线程都有一个独立的程序计数器,这块儿属于“线程私有”的内存
2. 虚拟机栈(Java栈)
1)(栈内存)为虚拟机执行java方法服务:方法被调用时创建栈帧–>局部变量表->局部变量、对象引用
2)每个方法被调用的时候都会创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息。局部变量表存放的是:编译期可知的基本数据类型、对象引用类型。

在这里插入图片描述

(1)局部变量表::存放了编译器可知的各种基本数据类型、对象引用(引用指针,并非对象本身),其中64位长度的long和double类型的数据会占用2个局部变量的空间,其余数据类型只占1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量是完全确定的,在运行期间栈帧不会改变局部变量表的大小空间

(2)操作数栈:存储方法内一些进行了运算操作后的结果,操作数栈就是JVM执行引擎的一个工作区

(3)动态链接:在方法内调用接口,通过字面量链接到具体的实现类,实现Java的动态特性

(4)方法出口(返回地址):return或者发生Exception等
3. 本地方法栈
1)为虚拟机执使用到的Native方法服务	

2)Java虚拟机没有对本地方法栈的使用和数据结构做强制规定,Sun HotSpot虚拟机就把java虚拟机栈和本地方法栈合二为一

3)本地方法栈也会抛出StackOverFlowError和OutOfMemoryError
4. 堆内存
1)存放所有new出来的东西
2)堆是java虚拟机所管理的内存区域中最大的一块,java堆是被所有线程共享的内存区域,在java虚拟机启动时创建,堆内存的唯一目的就是存放对象实例几乎所有的对象实例都在堆内存分配
3)堆是GC管理的主要区域,从垃圾回收的角度看,由于现在的垃圾收集器都是采用的分代收集算法,因此java堆还可以初步细分为新生代和老年代
4)Java虚拟机规定,堆可以处于物理上不连续的内存空间中,只要逻辑上连续的即可。在实现上既可以是固定的,也可以是可动态扩展的。如果在堆内存没有完成实例分配,并且堆大小也无法扩展,就会抛出OutOfMemoryError异常
5)其大小通过-Xms(最小值)和-Xmx(最大值)参数设置(最大最小值都要小于1G),前者为启动时申请的最小内存,默认为操作系统物理内存的1/64,后者为JVM可申请的最大内存,默认为物理内存的1/4,通常-Xms与-Xmx的值设成一样
6)我们平常所说的垃圾回收,主要回收的就是堆区。为了提升垃圾回收的性能,又把堆分成两块区新生代(young)和年老代(old),更细一点划分新生代又可划分为Eden区和2个Survivor区(From Survivor和To Survivor)

在这里插入图片描述

(1)Eden:新创建的对象存放在Eden区

(2)From Survivor和To Survivor:保存新生代gc后还存活的对象。(使用复制算法,导致有一个Survivor空间浪费,90%的利用率)Hotspot虚拟机新生代Eden和Survivor的大小比值为4:1,因为有两个Survivor,所以Eden:From Survivor:To Survivor比值为8:1:1

(3)老年代:对象存活时间比较长(经过多次新生代的垃圾收集,默认是15次)的对象则进入老年的。当堆中分配的对象实例过多,且大部分对象都在使用,就会报内存溢出异常(OutOfMemoneyError)
5. 方法区
1)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

2)Sun HotSpot虚拟机把方法区叫做永久代(Permanent Generation)(1.7以前),方法区中最终要的部分是运行时常量池

3)版本变化

(1)从JDK7开始移除永久代(但并没有移除,还是存在),贮存在永久代的一部分数据已经转移到了Java Heap或者是Native Heap:符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap

(2)JDK1.8对JVM架构的改造将类元数据放到本地内存中,另外,将常量池和静态变量放到Java堆里

在这里插入图片描述

6. 运行时常量池
1)运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时就会抛出OutOfMemoryError异常
7. 直接内存
1)直接内存并不是虚拟机内存的一部分,也不是Java虚拟机规范中定义的内存区域。jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小
8. 即时编译(JIT)
1)Java 字节码是解释执行的,但是没有直接在 JVM 宿主执行原生代码快。为了提高性能,Oracle Hotspot 虚拟机会找到执行最频繁的字节码片段并把它们编译成原生机器码。

2)编译出的原生机器码被存储在非堆内存的代码缓存中。通过这种方法,Hotspot 虚拟机将权衡下面两种时间消耗:将字节码编译成本地代码需要的额外时间和解释执行字节码消耗更多的时间

二、栈溢出和堆溢出示例

1. 栈溢出
public class Stack {

    public static void main(String[] args) {
        
        new Stack().test();
    }

    public void test() {
        test();
    }

}
2. 堆溢出(设置vm options参数: -Xms10M -Xmx10M -XX:+PrintGC)
public class Heap {

    public static void main(String[] args) {

        ArrayList list=new ArrayList();
        while(true) {
            Heap a = new Heap();
            System.out.println("a=" + a.toString());
            list.add(a);
            System.out.println("list大小" +  list.size());
        }

    }

}

参考网址

JVM内幕:Java虚拟机详解

JVM内存模型(jvm 入门篇)

Java虚拟机工作原理

栈帧、局部变量表、操作数栈

java方法区

equals()和hashCode()区别?

注:文章是经过参考其他的文章然后自己整理出来的,有可能是小部分参考,也有可能是大部分参考,但绝对不是直接转载,觉得侵权了我会删,我只是把这个用于自己的笔记,顺便整理下知识的同时,能帮到一部分人。
ps : 有错误的还望各位大佬指正,小弟不胜感激

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值