深入理解JVM JVM内存模型

1.JVM内存模型
        说起JVM内存模型,都是知道是Java方法区、Java栈、Native方法区、Java堆和程序计数器五部分,不过具体是做什么的,又有什么关系可能大家就不太清楚了,所以话不多说,直接上干货。

首先是JVM内存规范。

编译器和类加载在上篇博客已经讲了,不了解的可去看一下。现在主要就是运行时数据区了。具体请看下图

先给简单介绍一下什么是堆、桟、方法区以及格子

首先就是堆与栈分开设计是为什么呢?

栈存储了处理逻辑、堆存储了具体的数据,这样隔离设计更为清晰
堆与栈分离,使得堆可以被多个栈共享。
栈保存了上下文的信息,因此只能向上增长;而堆是动态分配
栈的大小可以通过-XSs设置,如果不足的话,会引起java.lang.StackOverflowError的异常

栈区
线程私有,生命周期与线程相同。每个方法执行的时候都会创建一个栈帧,用于存放 局部变量表、操作栈、动态链接、方法出口。


存放对象实例,所有的对象的内存都在这里分配。垃圾回收主要就是作用于这里的。

堆得内存由-Xms指定,默认是物理内存的1/64;最大的内存由-Xmx指定,默认是物理内存的1/4。
默认空余的堆内存小于40%时,就会增大,直到-Xmx设置的内存。具体的比例可以由-XX:MinHeapFreeRatio指定
空余的内存大于70%时,就会减少内存,直到-Xms设置的大小。具体由-XX:MaxHeapFreeRatio指定。
因此一般都建议把这两个参数设置成一样大,可以避免JVM在不断调整大小。

程序计数器
这里记录了线程执行的字节码的行号,在分支、循环、跳转、异常、线程恢复等都依赖这个计数器。程序寄存器取得就是程序计数器的值。

方法区
类型信息、字段信息、方法信息、其他信息

总结

名称 特征 作用 配置 异常
栈区 线程私有,使用一段连续的内存空间 存放局部变量表、操作栈、动态链接、方法出口 -XSs StackOverflowError OutOfMemoryError
线程共享,生命周期与虚拟机相同 保存对象实例 -Xms -Xmx -Xmn OutOfMemoryError
程序计数器 线程私有、占用内存小 字节码行号
方法区 线程共享 存储类加载信息、常量、静态变量等 -XX:PermSize -XX:MaxPermSize OutOfMemoryError

好了上面简单介绍了下,大家都有点印象了,现在就详细介绍下。

1. 程序计数器
1.1. 什么是程序计数器?

程序计数器就是在一块较小的空间,是当前线程的正在执行的字节码行号指示器,可以理解为程序计数器里记录的是当前线程执行的那条字节码指令的地址。

注:如果当前线程正在执行的是一个本地方法,那么此时程序计数器为空。

1.2. 程序计数器的作用

字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候,能够从程序计数器知道该线程上次运行到哪儿了。 
1.3. 程序计数器的特点

是一块较小的存储空间
线程私有。每条线程都有一个程序计数器。
是唯一一个不会出现OutOfMemoryError的内存区域。
生命周期随着线程的创建而创建,随着线程的结束而死亡。 
2. Java虚拟机栈(JVM Stack)
2.1. 什么是Java虚拟机栈?

Java虚拟机栈是描述Java方法运行过程的内存模型。

Java虚拟机栈会为每一个即将运行的Java方法创建一块叫做“栈帧”的区域,这块区域用于存储该方法在运行过程中所需要的一些信息,这些信息包括:

局部变量表 
存放基本数据类型变量、引用类型的变量、returnAddress类型的变量。

操作数栈
动态链接
方法出口信息

当一个方法即将被运行时,Java虚拟机栈首先会在Java虚拟机栈中为该方法创建一块“栈帧”,栈帧中包含局部变量表、操作数栈、动态链接、方法出口信息等。当方法在运行过程中需要创建局部变量时,就将局部变量的值存入栈帧的局部变量表中。

当这个方法执行完毕后,这个方法所对应的栈帧将会出栈,并释放内存空间。

虚拟机只会对java桟执行两种操作,即入栈和出栈。

注意:人们常说,Java的内存空间分为“栈”和“堆”,栈中存放局部变量,堆中存放对象。

这句话不完全正确!这里的“堆”可以这么理解,但这里的“栈”只代表了Java虚拟机栈中的局部变量表部分。真正的Java虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。

2.2. Java虚拟机栈的特点

局部变量表的创建是在方法被执行的时候,随着栈帧的创建而创建。而且,局部变量表的大小在编译时期就确定下来了,在创建的时候只需分配事先规定好的大小即可。此外,在方法运行的过程中局部变量表的大小是不会发生改变的。
Java虚拟机栈会出现两种异常:StackOverFlowError和OutOfMemoryError。 
a) StackOverFlowError:

若Java虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。

b) OutOfMemoryError:

若Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。

Java虚拟机栈也是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。
注:StackOverFlowError和OutOfMemoryError的异同?

StackOverFlowError表示当前线程申请的栈超过了事先定好的栈的最大深度,但内存空间可能还有很多。

而OutOfMemoryError是指当线程申请栈时发现栈已经满了,而且内存也全都用光了。

3. 本地方法栈
3.1. 什么是本地方法栈?

本地方法栈和Java虚拟机栈实现的功能类似,只不过本地方法区是本地方法运行的内存模型。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间。

也会抛出StackOverFlowError和OutOfMemoryError异常。

4. 堆
4.1. 什么是堆?

堆是最大的内存空间。

堆是用来存放对象的内存空间。

几乎所有的对象都存储在堆中。

4.2. 堆的特点

线程共享 
Java虚拟机只有一个堆,所有的线程都访问同一个堆。而程序计数器、Java虚拟机栈、本地方法栈都是一个线程对应一个的,属于线程私有的。

在虚拟机启动时创建
垃圾回收的主要场所。
可以进一步细分为:新生代、老年代。 
新生代又可被分为:Eden、 Survior0、Survior1。

不同区域存放具有不同生命周期的对象,存活时间不同。这样就可以根据不同的区域使用不同的垃圾回收算法,从而更具有针对性,从而更高效,也就是分代收集算法。

堆的大小既可以固定也可以扩展,但主流的虚拟机堆的大小是可扩展的,因此当线程请求分配内存,但堆已满,且内存已满无法再扩展时,就抛出OutOfMemoryError。
可使用-Xms -Xmx -Xmn设置最小堆内存,最大堆内存,年轻代大小, 建议把这两个堆内存参数设置成一样大,可以避免JVM在不断调整大小。

5. 方法区
5.1. 什么是方法区?

Java虚拟机规范中定义方法区是堆的一个逻辑部分。

方法区中存放已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。

5.2. 方法区的特点

线程共享 
方法区是堆的一个逻辑部分,因此和堆一样,都是线程共享的。整个虚拟机中只有一个方法区。

永久代 
方法区中的信息一般需要长期存在,而且它又是堆的逻辑分区,因此用堆的划分方法,我们把方法区称为老年代。

内存回收效率低 
方法区中的信息一般需要长期存在,回收一遍内存之后可能只有少量信息无效。

对方法区的内存回收的主要目标是:对常量池的回收 和 对类型的卸载。

Java虚拟机规范对方法区的要求比较宽松。 
和堆一样,允许固定大小,也允许可扩展的大小,还允许不实现垃圾回收。

5.3. 什么是运行时常量池?

方法区中存放三种数据:类信息、常量、静态变量、即时编译器编译后的代码。其中常量存储在运行时常量池中。

我们一般在一个类中通过 static final来声明一个常量。这个类被编译后便生成Class文件,这个类的所有信息都存储在这个class文件中。

当这个类被Java虚拟机加载后,常量就存放在方法区的运行时常量池中。而且在运行期间,可以向常量池中添加新的常量。如:String类的intern()方法就能在运行期间向常量池中添加字符串常量。

当运行时常量池中的某些常量没有被对象引用,同时也没有被变量引用,那么就需要垃圾收集器回收。

6. 直接内存(堆外内存)

直接内存是除Java虚拟机之外的内存,但也有可能被Java使用。

它可以通过调用本地方法直接分配Java虚拟机之外的内存,然后通过一个存储在Java堆中的DirectByteBuffer对象直接操作该内存,而无需先将外面内存中的数据复制到堆中再操作,从而提升了数据操作的效率。

直接内存的大小不受Java虚拟机控制,但既然是内存,当内存不足时就会抛出OOM异常。

直接内存(堆外内存)与堆内存比较

直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显
堆外内存,其实就是不受JVM控制的内存。相比于堆内内存有几个优势: 
  1 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到) 
  2 加快了复制的速度。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。 
  而福之祸所依,自然也有不好的一面: 
  1 堆外内存难以控制,如果内存泄漏,那么很难排查 
  2 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。
最后再给大家附上一张JVM执行流程图

结语
      好了,jvm内存模型就到这里,接下来会完善JVM垃圾回收的知识。

版权声明:本文为CSDN博主「残月十九」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37425816/article/details/81509998

原文地址:https://www.cnblogs.com/yss818824/p/12179466.html

时间: 2024-08-29 20:10:11

深入理解JVM JVM内存模型的相关文章

java中JVM虚拟机内存模型详细说明

java中JVM虚拟机内存模型详细说明 2012-12-12 18:36:03|  分类: JAVA |  标签:java  jvm  堆内存  虚拟机  |举报|字号 订阅 JVM的内部结构如下图: 一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能. 本文将从JVM内存模型.GC工作原理,以及GC的几个关键问题进行探讨,从

JVM的内存模型

JVM的内存模型 概述 Java虚拟机在执行java程序的过程中,会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁. java虚拟机所管理的内存包括以下几个运行时数据区域: 方法区(包括运行时常量池):存储类信息.常量.静态变量.即时编译器编译后的代码等:各种字面量和符号引用. Java堆:存储java对象. 虚拟机栈:存储方法以及方法中的存储局部变量表(存放基本数据类型

JVM运行时数据区与JVM堆内存模型小结

前提 JVM运行时数据区和JVM内存模型是两回事,JVM内存模型指的是JVM堆内存模型. 那JVM运行时数据区又是什么? 它包括:程序计数器.虚拟机栈.本地方法栈.方法区.堆. 来看看它们都是干嘛的 程序计数器:保存当前线程执行的指令的地址(大意如此). 虚拟机栈:由栈帧组成,而每个栈帧又包括局部变量表.操作数栈.动态连接(调用其他方法).出口(被调用时返回值) -- 每个栈帧就代表了一个方法的执行. 本地方法栈:类似虚拟机栈,只不过方法改成了native方法. 方法区:保存了类的各种信息.类的

【转】JVM虚拟机内存模型

转发声明: 本文原创作者:书呆子Rico 作者博客地址:http://blog.csdn.net/justloveyou_/ 摘要: 我们都知道,Java程序在执行前首先会被编译成字节码文件,然后再由Java虚拟机执行这些字节码文件从而使得Java程序得以执行.事实上,在程序执行过程中,内存的使用和管理一直是值得关注的问题.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域都有各自的用途,以及创建和销毁的时间,并且它们可以分为两种类型:线程共享的方

jvm Java内存模型

硬件的效率与一致性 在计算机中,内存的读写与处理器的计算速度有几个级的差距.这样会严重影响到TPS(Transations Per Second). 所以会为每个处理器配一个高速缓存以缓和处理器的速度.而在计算机中,多个处理器共享一个内存,这个时候数据的读写操作将不会安全 什么是内存模型                                                                        名词解释:JMM(Java Memory Model),即Java内

JVM虚拟机内存模型以及GC机制

JAVA堆的描述如下: 内存由 Perm 和 Heap 组成. 其中 Heap = {Old + NEW = { Eden , from, to } } JVM内存模型中分两大块,一块是 NEW Generation, 另一块是Old Generation. 在New Generation中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to), 它们用来存放每次垃圾回收后存活下来的对象.在Old Generation中,主要存放应用程序中生

JVM——java内存模型和线程

概述 计算机的运算速度与它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O.网络通信或者数据库访问上.我们当然不希望处理器大部分时间都处于等待其他资源的状态,要通过一些“手段”去把处理器的运算能力“压榨”出来,不然太浪费了. 衡量一个服务性能的高低好坏,每秒事务处理数(Transactions Per Second,TPS)是最重要的指标之一.代表一秒服务端平均能响应的请求总数,而TPS值与程序的并发能力又有密切的相关. 硬件的效率与一致性 处理器要和内存交互(取运算数据.存储运算

jvm的stack和heap,JVM内存模型,垃圾回收策略,分代收集,增量收集(转)

深入Java虚拟机:JVM中的Stack和Heap(转自:http://www.cnblogs.com/laoyangHJ/archive/2011/08/17/gc-Stack.html) 在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认识Stack和Heap,并通过这些原理认清Java中静态方法和静态属性的问题. 一般,JVM的内存分为两部分:Stack和Heap. Stack(栈)是JVM的内存指令区.Stack管理很简单,push

JVM内存模型及垃圾收集策略解析

AD: JVM内存模型是Java的核心技术之一,之前51CTO曾为大家介绍过JVM分代垃圾回收策略的基础概念,现在很多编程语言都引入了类似Java JVM的内存模型和垃圾收集器的机制,下面我们将主要针对Java中的JVM内存模型及垃圾收集的具体策略进行综合的分析. 一 JVM内存模型 1.1 Java栈 Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程.它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文.栈空间随着线程的终止而释放.St

JVM内存模型 三

本文章节: 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏 1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,有时候在开发Java同步和线程安全要求很严格的程序时,往往容易混淆的一个概念就是内存模型.究竟什么是内存模型?内存模型描述了程序中各个变量(实例域.静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和