java虚拟机:JVM内存模型

一、JVM内存模型图解

JVM 运行时数据区 (JVM Runtime Area) 其实就是指 JVM 在运行期间,其对JVM内存空间的划分和分配。网上找到两幅图如下所示(个人认为第二个图Native Method Stack应该画在Java Thead模块中):

    

二、各数据区域介绍

1、

栈分为java虚拟机栈和本地方法栈

  • 重点是Java虚拟机栈,它是线程私有的,生命周期与线程相同。
  • 每个方法执行都会创建一个栈帧,用于存放局部变量表,操作栈,动态链接,方法出口等。每个方法从被调用,直到被执行完。对应着一个栈帧在虚拟机中从入栈到出栈的过程。
  • 通常说的栈就是指局部变量表部分,存放编译期间可知的8种基本数据类型,及对象引用和指令地址。局部变量表是在编译期间完成分配,当进入一个方法时,这个栈中的局部变量分配内存大小是确定的。
  • 会有两种异常StackOverFlowError和 OutOfMemoneyError。当线程请求栈深度大于虚拟机所允许的深度就会抛出StackOverFlowError错误;虚拟机栈动态扩展,当扩展无法申请到足够的内存空间时候,抛出OutOfMemoneyError。
  • 本地方法栈为虚拟机使用到的本地方法服务(native),也是线程私有的。

2、堆区

  • 堆被所有线程共享区域,在虚拟机启动时创建,唯一目的存放对象实例。
  • 堆区是gc的主要区域,通常情况下分为两个区块年轻代和年老代。更细一点年轻代又分为Eden区最要放新创建对象,From survivor 和 To survivor 保存gc后幸存下的对象,默认情况下各自占比 8:1:1。 
    不过很多文章介绍分为3个区块,把方法区算着为永久代。这大概是基于Hotspot虚拟机划分, 然后比如IBM j9就不存在永久代概论。不管怎么分区,都是存放对象实例。
  • 会有异常OutOfMemoneyError

3、方法区

  • 被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanment generation)
  • 垃圾回收很少光顾这个区域,不过也是需要回收的,主要针对常量池回收,类型卸载。
  • 常量池用于存放编译期生成的各种字节码和符号引用,常量池具有一定的动态性,里面可以存放编译期生成的常量;运行期间的常量也可以添加进入常量池中,比如string的intern()方法。

4、程序计数器

  • 当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。
  • Java虚拟机多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换能恢复到正确的位置,每条线程都需要一个独立的程序计数器,所以它是线程私有的。
  • 唯一一块Java虚拟机没有规定任何OutofMemoryError的区块

三、数据区域汇总


名称


特征


作用


配置参数


异常


程序计数器


占用内存小,线程私有,生命周期与线程相同


大致为字节码行号指示器




虚拟机栈


线程私有,生命周期与线程相同,使用连续的内存空间


Java 方法执行的内存模型,存储局部变量表、操作栈、动态链接、方法出口等信息


-Xss


StackOverflowError

OutOfMemoryError


java堆


线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址


保存对象实例,所有对象实例(包括数组)都要在堆上分配


-Xms

-Xsx

-Xmn


OutOfMemoryError


方法区


线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址


存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据


-XX:PermSize:16M

-XX:MaxPermSize:64M


OutOfMemoryError


运行时常量池


方法区的一部分,具有动态性


存放字面量及符号引用

四、延伸:直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError 异常出现,所以我们放到这里一起讲解。

在JDK 1.4 中新加入了NIO(NewInput/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java 堆里面的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java 堆和Native 堆中来回复制数据。

五、延伸:堆与栈的对比

经常有人把Java 内存区分为堆内存(Heap)和栈内存(Stack),这种分法比较粗糙,Java内存区域的划分实际上远比这复杂。这种划分方式的流行只能说明大多数程序员最关注的、与对象内存分配关系最密切的内存区域是这两块。

堆很灵活,但是不安全。对于对象,我们要动态地创建、销毁,不能说后创建的对象没有销毁,先前创建的对象就不能销毁,那样的话我们的程序就寸步难行,所以Java中用堆来存储对象。而一旦堆中的对象被销毁,我们继续引用这个对象的话,就会出现著名的 NullPointerException,这就是堆的缺点——错误的引用逻辑只有在运行时才会被发现。

栈不灵活,但是很严格,是安全的,易于管理。因为只要上面的引用没有销毁,下面引用就一定还在,在大部分程序中,都是先定义的变量、引用先进栈,后定义的后进栈,同时,区块内部的变量、引用在进入区块时压栈,区块结束时出栈,理解了这种机制,我们就可以很方便地理解各种编程语言的作用域的概念了,同时这也是栈的优点——错误的引用逻辑在编译时就可以被发现。

栈--主要存放引用和基本数据类型。

堆--用来存放 new 出来的对象实例。

参考:http://blog.csdn.net/u012152619/article/details/46968883

时间: 2024-10-18 16:37:13

java虚拟机:JVM内存模型的相关文章

Java虚拟机:内存模型详解

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实例的时候,虚拟机要为对象分配内存,Java虚拟机又是如何配分内存的呢?这些都涉及到Java虚拟机的内存划分机制,今天我们就来探究一下Java虚拟机的内存模型. Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间,有的区域随

java虚拟机之内存模型

1. 概述 对于从事 C.C++ 程序开发的人员来说,在内存管理领域,他们既是拥有最高权力的「皇帝」又是从事基础工作的「劳动人民」 --- 既拥有每个对象的「所有权」,又担负着每一个对象生命开始到终结的维护责任. 但是对于 java 程序员来说,在虚拟机自动内存管理机制的帮助下,不需要再为每一个 new 操作写配对的 delete/free 代码,不容易出现在内存泄漏和内存溢出问题,由虚拟机管理内存这一切看起来都很美好.不过,也正是因为 java 程序员把内存控制的权利交给了 java 虚拟机,

Java虚拟机的内存模型

一.问题起源 这篇文章的起源其实是博主想要学习Java并发,计算机并发的痛点在于一方面cpu越来越多核化,另外 一个方面就是cpu和存储以及通信子系统的速度差距太大,大概来说,cpu速度是ns级,内存100ns,硬盘ms,内存比cpu慢100倍, 硬盘比cpu慢100万倍,cpu如果-等这些速度慢的多的小伙伴,势必造成计算资源的浪费.物理机为了解决cpu与内存的速度差, 在两者之间加入了缓存,有缓存就会引入新问题,即缓存一致性问题. 图1(图片来自参考文献[1]) 为了解决缓存一致性问题,各个c

深入Java虚拟机——JVM内存详解

在C++中,程序员拥有每一个对象的所有权,但与此同时还肩负着释放对象内存空间的责任:而Java由于有了虚拟机的帮助,程序员拥有对象的所有权的同时不再需要释放对象的内存空间.由于是JVM自动进行对象内存的释放,所以内存泄漏和内存溢出的问题也很少出现. Java虚拟机在运行时将内存空间分成5个部分,分别是:方法区.虚拟机栈.本地方法栈.堆.程序计数器. 程序计数器 本质 程序计数器本质上是一块较小的内存空间. 作用 可以把程序计数器简单地看作是当前线程所执行的字节码的行号指示器. 字节码解释器在工作

深入理解Java虚拟机笔记---内存模型

主内存与工作内存 Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量值这样的底层细节.此处的变量(Variable)与Java编译中所说的变量略有区别,它包括了实例字段,静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不存在竞争的问题.了为获得比较好的执行效率,Java内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器调整代码执行顺序这类权限. J

JAVA补缺--JVM内存模型

1.内存分区. 内存区域分为五块:方法区,虚拟机栈,堆,本地方法栈,程序计数器. 方法区:线程共享的内存区域.存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译的代码数据等.而并非存储的方法代码. 虚拟机栈:JAVA中描述JAVA方法执行的内存模型.每个方法创建的时候都会创建一个栈帧,栈帧用于存储:方法的局部变量,操作数,动态链接,方法出口等信息.每个方法的执行过程就是栈帧从入栈到出栈的过程.即方法执行完毕后,栈帧即被销毁. 堆:同方法区是线程共享的内存区域.所有的(new的时候)对象实

如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习<深入理解java虚拟机-JVM高级特性与最佳实践>这本书,终于初步了解了一下java虚拟机的内存模型.本文通过写出使jvm发生内存溢出异常的代码来对自己的学习结果进行总结,同时也提醒自己以后写代码时候不要再跳进这个坑啦. java的内存管理是由java虚拟机自动进行管理的,并不需要程序员过多的手动干预

深入理解JVM(一)——JVM内存模型

JVM内存模型 Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: 1. 程序计数器 2. Java虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区. 下面对这五个区域展开深入的介绍. 1. 程序计数器 1.1. 什么是程序计数器? 程序计数器是一块较小的内存空间,可以把它看作当前线程正在执行的字节码的行号指示器.也就是说,程序计数器里面记录的是当前线程正在执行的那一条字节码指令的地址. 注:但是,如果当前线程正在执行的是一个本地方法,那么此时程

JVM内存模型与类加载机制

一. java虚拟机的内存模型如图: 补习一下jvm内存模型中的各个组成部分 堆: 我们new出来的对象全部放在堆中,他是jvm所能够动态分配的最大的一块空间 优点: 内存动态分配,生命周期不必事先告诉编译器,由JAVA的垃圾回收线程动态回收 栈: 存取的速度仅次于CPU的寄存器,但是存在栈里面的数据大小必须是提前定义的比如大家都知道int 4个字节 jvm在调用某些方法时,方法的参数,以及局部变量就存放在栈里面,(因为它们符合内存空间已知的特性) 主函数在栈的最底部 栈是有线程特性的,每个线程