一、 基本概念

每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私有的栈。进程所创建的所有类的实例(也就是对象)或数组(指的是数组的本身,不是引用)都放在堆中,并由该进程所有的线程共享。Java中分配堆内存是自动初始化的,即为一个对象分配内存的时候,会初始化这个对象中变量。虽然Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也就是说在建立一个对象时在堆和栈中都分配内存,在堆中分配的内存实际存放这个被创建的对象的本身,而在栈中分配的内存只是存放指向这个堆对象的引用而已。局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。

具体的概念:JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method,也叫静态区):

堆区: 

1.存储的全部是对象,每个对象都包含一个与之对应的class的信息(class的目的是得到操作指令) ;
2.jvm只有一个堆区(heap),且被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身和数组本身;

栈区: 
1.每个线程包含一个栈区,栈中只保存基础数据类型本身和自定义对象的引用;
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问;
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令);

方法区(静态区): 
1.被所有的线程共享,方法区包含所有的class(class是指类的原始代码,要创建一个类的对象,首先要把该类的代码加载到方法区中,并且初始化)和static变量。 
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

二、实例演示

 1   AppMain.java 2  3   public   class  AppMain                //运行时, jvm 把appmain的代码全部都放入方法区     4   {     5   public   static   void  main(String[] args)  //main 方法本身放入方法区。     6   {     7   Sample test1 = new  Sample( " 测试1 " );   //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面     8   Sample test2 = new  Sample( " 测试2 " );     9   
10   test1.printName();
11   test2.printName();
12   }
13   }
14   
15   public   class  Sample        //运行时, jvm 把appmain的信息都放入方法区
16   {
17   /** 范例名称 */
18   private String name;      //new Sample实例后, name 引用放入栈区里, name 对应的 String 对象放入堆里
19   
20   /** 构造方法 */
21   public  Sample(String name)
22   {
23   this .name = name;
24   }
25   
26   /** 输出 */
27   public   void  printName()   //在没有对象的时候,print方法跟随sample类被放入方法区里。
28   {
29   System.out.println(name);
30   }
31   }    

运行该程序时,首先启动一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中,这就是AppMain类的加载过程。

接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是:

  Sample test1=new Sample("测试1"); 

该语句的执行过程:
    1、 Java虚拟机到方法区找到Sample类的类型信息,没有找到,因为Sample类还没有加载到方法区(这里可以看出,java中的内部类是单独存在的,而且刚开始的时候不会跟随包含类一起被加载,等到要用的时候才被加载)。Java虚拟机立马加载Sample类,把Sample类的类型信息存放在方法区里。 
    2、 Java虚拟机首先在堆区中为一个新的Sample实例分配内存, 并在Sample实例的内存中存放一个方法区中存放Sample类的类型信息的内存地址。
    3、 JVM的进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。

4、位于“=”前的Test1是一个在main()方法中定义的一个变量(一个Sample对象的引用),因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例。
    5、JVM在堆区里继续创建另一个Sample实例,并在main方法的方法调用栈中添加一个Test2变量,该变量指向堆区中刚才创建的Sample新实例。

6、JVM依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,定位到方法去中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令,开始执行。

三、辨析

 

在Java语言里堆(heap)和栈(stack)里的区别 :

1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 
  2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享(详见下面的介绍)。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

Java中的2种数据类型:

  一种是基本类型(primitive types), 共有8类,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。 
  

栈有一个很重要的特性:存在栈中的数据可以共享。假设我们同时定义:  int a = 3;  int b = 3;  编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,如果没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。

  这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。 
  另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

java运行时内存分配详解相关推荐

  1. JVM 运行时内存空间详解——元空间

    通过上一篇文章,我们大体了解了JVM的整体架构,其分为:元数据(JDK7是方法区).堆.虚拟机栈.本地方法栈.程序计数器几个部分. 本篇文章,咱们对元空间进行剖析,一探究竟. 1. 元空间介绍 在JD ...

  2. WCF服务端运行时架构体系详解[下篇]

    作为WCF中一个核心概念,终结点在不同的语境中实际上指代不同的对象.站在服务描述的角度,我们所说的终结点实际上是指ServiceEndpoint对象.如果站在WCF服务端运行时框架来说,终结点实际上指 ...

  3. spark on yarn 内存分配详解

    spark on yarn 内存分配详解

  4. 深入理解java虚拟机一 JAVA运行时内存区域与class文件

    一 JAVA运行时内存区域 JVM在加载class文件时,会将class文件定义的数据结构转为运行时内存中的数据,那么jvm是如何安排运行时的内存区域呢? jvm将运行时内存划分为以下几个部分: 堆: ...

  5. [转载] JAVA 堆栈 堆 方法区 静态区 final static 内存分配 详解

    参考链接: 在Java中为静态最终static final变量分配值 转载来源:https://blog.csdn.net/peterwin1987/article/details/7571808 J ...

  6. JVM 运行时数据区详解,写得非常好!

    作者:小小木的博客 www.cnblogs.com/wyc1994666/p/11795781.html 在接下来的几天想总结下,JVM相关的一些内容,比如下面的这三个内容算是比较核心知识点了 1.运 ...

  7. Java运行时内存工作过程

    介绍在Java8中,运行时内存是如何工作的,对象的走向如何,Java7和Java8内存模型的变化又是什么?接下来让我给你解开谜团 运行时内存 基本组成如图: 其中新生代又分为Eden区.Survivo ...

  8. java运行时内存模型

    运行时内存分为: 1.方法区 2.堆 3.虚拟机栈 4.本地方法栈 5.程序计数器 方法区.堆是共享的,所有线程都可以读取 虚拟机栈.本地方法栈.程序计数器是线程私有的,每个线程单独一套,它们在线程创 ...

  9. Java JVM内存模型(运行时数据区域)详解

    详细介绍了JVM运行时数据区域,包括方法区.堆空间.栈空间.本地方法栈.程序计数器.常量池.直接内存.字面量.符号引用.直接引用. Java程序在运行时,需要在内存中的分配空间.为了提高运算效率,ja ...

最新文章

  1. canvas学习笔记(下篇) -- canvas入门教程--保存状态/变形/旋转/缩放/矩阵变换/综合案例(星空/时钟/小球)...
  2. 使用nginx在Windows部署网站https服务
  3. Random Forests
  4. Ubuntu SSH Algorithm negotiation failed
  5. Android7.0,剪裁后提示“无法保存经过裁剪的图片”
  6. Native App
  7. Yam旗下Degenerative Finance已上线uSTONKS和uGAS奖励计划
  8. pandas合并数据集-【老鱼学pandas】
  9. android中jni的调用过程,android JNI学习② JNI调用过程
  10. 互联网晚报 | 8月22日 星期日 | 抖音回应腾讯《扫黑风暴》相关投诉;比亚迪半导体被中止上市审核;三星正式推出UPC技术...
  11. 电脑的Windows图片查看器找不到怎么办
  12. (二)Chrome新标签页的设置
  13. Python求向量的余弦值
  14. javscript创建Emitter
  15. JVM - 进入Java虚拟机的真实世界
  16. React Native实践之携程Moles框架
  17. 【quill-editor】富文本编辑器对图片的放大缩小拖拽的实现
  18. 哔哩哔哩(B站)的前端之路
  19. cocos2d-x传智播客_Hanselminutes播客167-与Jeremy Miller进行的配置约定
  20. 瘦大腿怎么瘦?上班族怎么瘦大腿?

热门文章

  1. ado.net的操作查询数据 0127
  2. 学习日报 1026 使用属性升级MyBank
  3. xlwt边框与边框颜色
  4. 前端开发 get请求与post请求 0228
  5. git-工作区与暂存区
  6. django-模板加载顺序-随堂
  7. Mysql update 使用join更新字段
  8. c++_导入/导出excel文件
  9. PHP安装imagemagick扩展imagick
  10. MapReduce编程实践之自定义数据类型