zoukankan      html  css  js  c++  java
  • JVM内存结构简单认知

    关于JVM的面试传送门:https://blog.csdn.net/shengmingqijiquan/article/details/77508471

    JVM内存结构主要划分为:堆,jvm栈,本地方法栈,方法区,程序计数器

    如下图所示:

    堆区:

    简单概述:每个Java项目都有唯一对应的一个JVM实例,每一个JVM实例又对应着一个堆区。Java堆是被当前应用所有进程所共享的,在JVM启动时就创建了。堆区的目的就是存放所有new创建实例对象和数组,由此可见堆对于当前应用来说是全局的。

      PS:这也就解释了假设有A,B两个Java项目,A项目有a,b,c三个类,a类可以调用b,c三个类,但是在B项目就不可以使用a,b,c三个类了

    堆区的特点堆的主要优势是灵活性,它可以的动态的分配内存。编译器不需要知道需要创建对象的时候需要分配多少内存给它,也不需要知道该对象的生命周期。new一个对象的时候会自动从堆中分配内存给它,然后如果长时间没有引用指向该对象时,JVM垃圾回收机制会在某个合适的时间点回收它。

    Java堆是jvm管理内存中最大的一块,它也是JVM垃圾回收的主要区域。虽然JVM垃圾回收机制会在某个合适的时间点自动回收没有引用指向的对象,但是作为java开发人员,掌握垃圾回收机制是很重要的,日后还要去学习这个....

    本地方法栈:

    虚拟机执行Native方法服务,执行不是用Java代码写的如C写的

    虚拟机栈区:

      当程序进入到一个方法时,会为这个方法分配一个私有存储空间,该空间为JVM栈。该线程为私有的。就好比你在a()方法中定义了int t = 1;但是在b方法中就不能直接用t。但是栈里面的数据是可以共享的。当方法结束时,该区域就释放了

      栈的速度比堆要快,仅次于寄存器。但是缺少灵活性,其数据大小和生存周期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了)

      至于为什么函数调用要采用栈调用,主要是契合数据结构中栈的先进后出原则,可以看下这篇连接:https://www.zhihu.com/question/34499262/answer/59415153

      实例分析一:栈里面的数据是共享的

      int a=1;

      int b=1;

      编译器预先处理int a =1;首先在栈中创建一个变量为a的引用,然后去栈中查找有没有字面值为1的地址,没有就开辟一个存放字面值为1的地址。接着执行b=1;创建一个变量为b的引用,由于已经存在字面值为1的地址了,所以b直接指向该地址。所以:出现了a,b同时指向1这个地址

      特别注意:字面值的引用与对象的引用是不一样的,假设两个类对象的引用指向同一个对象,如果一个引用对象修改了这个类的内部状态,那么另一个引用对象也会即可反映出这个变化;而字面值引用则不会,假设在写上b=2,那么b引用会重新去JVM栈中去寻找字面值为2的地址,找不到则重新开辟一个地址来存储2,然后b引用在指向它

    public class test2 {
    
        public static void main(String[] args) {
            int a = 1;
            int b = 1;
            // 1.创建变量为a的引用,在JVM栈中开辟字面值为1的地址,a在指向它
            // 2.创建变量为b的引用,栈中已存在字面值为1的地址,b直接指向该地址。所以a,b地址相同
            System.out.println(a == b);    //true
            b = 2;
            System.out.println(a==b);    //false
        }
    
    }
    public class test {
    
        int num = 1;
    
        public static void main(String[] args) {
            // 创建变量为a的对象引用,new test()在堆中分配内存存储该对象,a再指向堆区中该对象的地址
            test a = new test();
            // 创建变量值为b的引用对象,指向a指向的堆区中地址
            test b = a;
            System.out.println(a == b); 
            b.num = 2;
            //这里就属于另一个对象引用修改了该对象的内部状态,导致a受影响了
            System.out.println(a.num); // 2 如果是test b = new test()则输出1,因为指向的不是同一个对象
    
            // 这里又在堆区中给test对象分配了内存
            test c = new test();
            System.out.println(a == c); 
        }
    
    }

     关于包装类型数据:如Integer,Double,Float等将基本数据类型包装起来的类,它们存在于堆区中。Java用new()来显示的告诉编译器要创建。之后将对包装类型做一下整理,尤其是地址指向

    http://www.blogjava.net/hgc-ghc/archive/2013/05/02/398675.html

    方法区:

      方法区也同堆一样是被所有线程所共享的,它里面存放着satic修饰的代码和类信息,常量(final修饰)

    程序计数器:

    线程私有,占用内存小。

    字节码行号指示器,解释器通过它来选取下一条执行的字节码指令

    • 执行 Java 方法:具体的内容就是指向下一个指令的偏移
    • 执行 Native 方法:计数值为空(undefined)

    不会有 OutOfMemoryError

  • 相关阅读:
    第三次作业
    利用LINQ to SQL 增删改查本地数据库
    【转】C#,回车,换行
    【转】C#3.0编码习惯与命名规则
    Connect to the DSP on C6A8168/DM8168/DM8148 using CCS
    【转】OpenCV 移植学习EMCV
    目标跟踪小结
    利用TortoiseSVN获取最新版本的OpenCV源码
    转图像灰度化方法总结及其VC实现
    OpenCV读取视频的格式注意点
  • 原文地址:https://www.cnblogs.com/zengcongcong/p/10781394.html
Copyright © 2011-2022 走看看