zoukankan      html  css  js  c++  java
  • 一:jvm的五大内存区(内存结构)

    五大内存区  

    jvm五大内存区域(即jvm运行时数据区),描述的是类被加载时,经过解析后,存储到特定的数据区。方法区和堆是所有线程共享的,而栈和计数器是线程私有的。栈处理程序运行的问题,堆处理数据的存储问题。所以才有堆栈分离。

    方法区:又被称为元空间,用来存储类的信息,例如:方法,方法名,返回值,常量。当它无法满足内存分配需求时,方法区会抛出OutOfMemoryError。

    :存放new出来的对象信息, 全局变量。

    程序计数器:指向当前线程正在执行的行号,用来保证线程切换时回到程序调用的位置。(例如:在a方法里面掉用了b方法,代码从上往下执行,执行到掉用b方法的那行时,指针会记录下这个位置,然后执行b方法里面的逻辑,b方法正常执行完或异常退出,指针都会回到a方法里面。)

    案例:我们这里可以写一个java文件,通过javac命令编译生成一个class文件,然后通过javap -l 命令查看程序的行号和局部变量表。

    本地方法栈:和虚拟机栈类似,只是它描述的是为虚拟机是用到的Native方法出栈和入栈的过程(通常我们不需要了解这块,它底层是C语言实现的)。

    虚拟机栈:描述的是线程进栈出栈的过程,线程结束内存自动释放。它用来存储当前线程运行方法所需要的数据、指令、返回地址。(即局部变量和正在调用的方法)     方法被调用时会在栈中开辟一块叫栈帧的空间,方法运行在栈帧空间中。栈帧出栈后,里面的局部变量直接就从内存里清理掉了。

    先进后出

    // main方法先入栈,然后程序从上往下执行,a 和 b 相继入栈,程序执行完之后出栈,可以看到main方法最后执行完,所以栈是先进后出的。
    
    public class Test {
    
        public static void main(String[] args) {
            a();
            System.out.println("main...");
        }
    
        public static void a(){
            b();
            System.out.println("a...");
        }
    
        public static void b(){
            System.out.println("b...");
        }
    }
    
    ==========  控制台输出  ===========
    
    b...
    a...
    main...

    栈帧的逻辑内存图里面包含局部变量表,操作数栈,动态链接,出口...(注意:栈帧里面包含但不仅仅只有这些, 程序员开发主要是关注这些 )。  

    public static void demo(){
        /**
         * 【局部变量表】 存放该方法的参数变量,和方法内部定义的局部变量,若该变量为引用类型,则存的该变量的引用地址。在java文件编译成.class文件的时候,这个表的容量最大值就确定下来了。
         */
        String a = "局部变量a";
        String b = "局部变量b";
        int c = 2,d = 3; // 局部变量c和d
        /**
         * 【操作数栈】 也称为表达式栈,通过字节码指定把值压入栈定,稍后另一个指令就可以弹出这个值使用。
         */
        int e = c*d; // 操作数栈
        /**
         * 【动态链接】 也称为常量池。用来保存常量值和符号引用。符号引用:A方法在运行时调用B方法,就是通过B方法的符号引用去找到B的内存地址.)
         */
        UserDao dao = new UserDao();
        dao.insert(a);
        /**
         * (出口):描述的就是出栈的过程。方法的返回有两种情况。(正常退出时:根据方法的定义来决定是否要传返回值给上层调用者。异常退出时:则是需要通过异常处理来确定。无论通过哪种方式退出,都会跳到当前方法被调用的位子。
         */
    }

    * 如果线程请求的栈深度⼤大于虚拟机所允许的深度,将抛出 StackOverflowError 异常

      

    代码运行流程

    比如现在有这么一段代码

      

    1. 首先main线程会来执行User类的main方法。main线程会有自己的一个虚拟机栈,他会把main方法的栈帧压入虚拟机栈中。

      

    2. 然后main()方法里面又调用了loadUserId()方法,此时就会创建一个loadUserId()方法的栈帧,并压入main线程的虚拟机栈中。

       

    3. 此时loadUserId()方法中,有一个局部变量 order 

       

    4. 然后创建了一个 Order 类的实例对象,此时就会在java堆内存中分配这个实例对象的内存空间。并且order变量会指向Order实例对象的内存空间

      

    5. 最后就会通过局部变量 order 引用Order的实例对象去执行它的getUserId()方法了

  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/wlwl/p/9463557.html
Copyright © 2011-2022 走看看