博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM内存模型
阅读量:5213 次
发布时间:2019-06-14

本文共 2941 字,大约阅读时间需要 9 分钟。

在工作中总会时不时与JVM打交道,尤其是在做性能调优或是看见OOM时,就需要对jvm启动参数做些调整,而我总是头疼于那些参数的含义,特记之。

 

引用博文:

The Java Memory Architecture 

JVM内存管理总结 

JVM内存管理-深入垃圾收集器与内存分配策略 

JVM内存管理-深入Java内存区域与OOM 

图解JVM内存模型  

图解JVM在内存中申请对象及垃圾回收流程  

一次Java垃圾收集调优实战 

JVM参数表 

JVM的内部结构如下图:
 
JVM主要包括两个子系统和两个组件:

 

 

1. 两个子系统分别是Class loader子系统和Execution engine(执行引擎) 子系统;

 

1.1 Class loader子系统的作用:根据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area中的method area(方法区域)。Java程序员可以extends java.lang.ClassLoader类来写自己的Class loader。

 

1.2 Execution engine子系统的作用:执行classes中的指令。任何JVM specification实现(JDK)的核心都是Execution engine,不同的JDK例如Sun 的JDK 和IBM的JDK好坏主要就取决于他们各自实现的Execution engine的好坏。

 

2. 两个组件分别是Runtime data area (运行时数据区域)组件和Native interface(本地接口)组件。

 

2.1 Native interface组件:与native libraries交互,是其它编程语言交互的接口。当调用native方法的时候,就进入了一个全新的并且不再受虚拟机限制的世界,所以也很容易出现JVM无法控制的native heap OutOfMemory。

 

2.2 Runtime Data Area组件:这就是我们常说的JVM的内存了。它主要分为五个部分——

1、Heap (堆):一个Java虚拟实例中只存在一个堆空间,Java堆是被所有线程共享的,在虚拟机启动时创建。Java堆的唯一目的就是存放对象实例,绝大部分的对象实例都在这里分配。Java堆内还有更细致的划分:新生代、老年代,再细致一点的:eden、from survivor、to survivor,甚至更细粒度的本地线程分配缓冲(TLAB)等,无论对Java堆如何划分,目的都是为了更好的回收内存,或者更快的分配内存。

Java堆可以处于物理上不连续的内存空间,它逻辑上是连续的即可,就像我们的磁盘空间一样。实现时可以选择实现成固定大小的,也可以是可扩展的,不过当前所有商业的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。如果在堆中无法分配内存,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

 

2、Method Area(方法区域):被装载的class的信息存储在Method area的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。叫“方法区”可能认识它的人还不太多,如果叫永久代(Permanent Generation)它的粉丝也许就多了。它还有个别名叫 做Non-Heap(非堆)。

方法区中存放了每个Class的结构信息,包括常量池、字段描述、方法描述等等。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量表(constant_pool table),用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的String.intern()方法)。

运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法在申请到内存时会抛出OutOfMemoryError异常。

 

3、Java Stack(java的栈):虚拟机只会直接对Java stack执行两种操作:以帧为单位的压栈或出栈。栈描述的是Java方法调用的内存模型:每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。

4、Program Counter(程序计数器):每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。PC寄存器的内容总是指向下一条将被执行指令的饿地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。

  

5、Native method stack(本地方法栈):保存native方法进入区域的地址.
以上五部分只有Heap 和Method Area是被所有线程的共享使用的;而Java stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有自己的部分。

 

此外还有本机直接内存的管理(Direct Memory) -- 直接内存并不是虚拟机运行时数据区的一部分,它根本就是本机内存而不是VM直接管理的区域。

显然本机直接内存的分配不会受到Java堆大小的限制,但是即然是内存那肯定还是要受到本机物理内存(包括SWAP区或者Windows虚拟内存)的限制的,一般服务器管理员配置JVM参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError异常。

 

 

 

JVM内存模型实例以及参数对应:

 

 

  • Model1
  • Model2
  • 对照表:

Model-1

Model-2

Exception

JVM Options

Method Area

Perm

java.lang.OutOfMemoryError: PermGen space

-XX:PermSize=<value>

-XX:MaxPermSize=<value>

Heap

Young Tenured

java.lang.OutOfMemoryError: Java heap space

-Xms<size>

-Xmx<size>

-Xmn<size>

-XX:newSize

-XX:MaxNewSize

-XX:NewRatio=<value>

-XX:SurvivorRatio=<value>

Thread-1…N

NULL

-Xss<size>

*Memory Size of Runtime JVM = Heap + Perm + Sum(Thread-1...N)

来自:

转载于:https://www.cnblogs.com/snowberg/archive/2011/11/29/2468564.html

你可能感兴趣的文章
linux 命令大全
查看>>
命令模式
查看>>
897.Increasing Order Search Tree
查看>>
HTMLDiary
查看>>
2.JVM的参数配置
查看>>
iOS下获取用户当前位置的信息
查看>>
Ubuntu中安装(升级)GraphicsMagick
查看>>
防火墙
查看>>
css3动画animation
查看>>
eclipse安装wtp
查看>>
(转)C#中属性PropertyInfo的使用,Dictionary转为Model实例
查看>>
2017NOIP游记
查看>>
学习yii2.0框架阅读代码(二)
查看>>
Windows Apache2.4 配置https/SSL
查看>>
PHP常用魔术方法(__call魔术方法:)
查看>>
vue 工作学习总结
查看>>
REST总结
查看>>
Hibernate中的session和load延迟载入矛盾问题,怎样解决?
查看>>
log4j:WARN Please initialize the log4j system properly解决的方法
查看>>
《Google软件测试之道》测试工程师
查看>>