zoukankan      html  css  js  c++  java
  • java基础>java虚拟机运行环境数据区域说明

    堆(heap)、栈(stack)和方法区(method)

     

    JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method区)

    堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。Java中分配堆内存是自动初始化的。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量。 Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。数组一样,数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小!

    一、堆区(heap):

    1、每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享。存储的全部是对象,每个对象实例都包含一个与之对应的class的信息的引用。(而class信息存储在方法区域,class的目的是得到操作指令),

    2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

    3.this 在堆空间分配 。

    二、栈区(stack):

    存储局部变量和返回结果,与方法的调用和返回有关。

    1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中

    2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。

    3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。 

    三、方法区:

    1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class的信息和static变量。如class,static变量,它们都是在整个程序中永远唯一的元素,。

    2.Method Area由所有的jvm线程共享。它存储每个Class的结构,比如说运行时常量池、域、方法数据、方法体、构造函数、包括类中的专用方法、实例初始化、接口初始化。其中的Runtime Constant Pool是代表运行时每个class文件中的常量表。每个Runtime Constant pool都是在jvm的method area中分配的,每个class或者interface的constant pool都是在jvm创建class或接口的时候创建的。

    3、字符串常量在 DATA 区域分配 。

    四、例子说明

    为了更清楚地搞明白发生在运行时数据区里的黑幕,以下面2个非常简单的小程序为例。
    AppMain.java

    public class AppMain //运行时, JVM把AppMain的信息都放入方法区 
    {
    	public static void main(String[] args) //main方法本身放入方法区。 
    	{
    		Sample test1 = new Sample(" 测试1 "); //test1是引用,所以放到栈区里, 产生的Sample对象内容应该放到堆里面 
    		Sample test2 = new Sample(" 测试2 ");
    		test1.printName();
    		test2.printName();
    	}
    }

    Sample.java

    public class Sample //运行时, jvm把Sample的信息都放入方法区 
    {
    	/** 范例名称 */
    	private String name; //new Sample实例后, name引用放入栈区里,name对象放入堆里 
    
    	/** 构造方法 */
    	public Sample(String name) {
    		this.name = name;
    	}
    
    	/** 输出 */
    	public void printName() //print方法本身放入方法区里。 
    	{
    		System.out.println(name);
    	}
    }

    当执行指令“java AppMain”时,系统收到了我们发出的指令,启动了一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中。这一过程称为AppMain类的加载过程。
    接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是: Sample test1=new Sample("测试1"); 语句很简单,就是让java虚拟机创建一个Sample实例,并且呢,使引用变量test1引用这个实例。

    1、 Java虚拟机一看,不就是建立一个Sample实例吗,简单,于是就直奔方法区而去,先找到Sample类的类型信息再说。结果呢,没找到,这会儿的方法区里还没有Sample类呢。于是,它立马加载了Sample类,把Sample类的类型信息存放在方法区里。
    2、下面,Java虚拟机做的第一件事情就是在堆区中为一个新的Sample实例分配内存, 这个Sample实例持有着指向方法区的Sample类的类型信息的引用。这里所说的引用,实际上指的是Sample类的类型信息在方法区中的内存地址,其实,就是有点类似于C语言里的指针,而这个地址呢,就存放了在Sample实例的数据区里。
    3、 在JAVA虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。OK,原理讲完了,就让我们来继续我们的跟踪行动!位于“=”前的Test1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例,也就是说,它持有指向Sample实例的引用。
    OK,到这里为止呢,JAVA虚拟机就完成了这个简单语句的执行任务。

    4、接下来,JAVA虚拟机将继续执行后续指令,在堆区里继续创建另一个Sample实例,然后依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,定位到方法区中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令。

     

    参考:java Runtime Data Areas(java虚拟机运行环境数据区域说明)

          java堆、栈、方法区的理解
     

  • 相关阅读:
    Minimum Depth of Binary Tree leetcode java
    Maximum Depth of Binary Tree leetcode java
    Symmetric Tree leetcode java
    Same Tree leetcode java
    Binary Tree Postorder Traversal leetcode java
    Binary Tree Preorder Traversal leetcode java
    Binary Tree Inorder Traversal leetcode java
    Combinations leetcode java
    一键清除Centos iptables 防火墙所有规则
    阿里云centos7.7x64安装open,并配置ip转发和nat伪装
  • 原文地址:https://www.cnblogs.com/xqzt/p/5637303.html
Copyright © 2011-2022 走看看