赞
踩
JVM运行时数据区如下图:
整个JVM构成里面,主要由三部分组成:类加载系统、运行时数据区、执行引擎。
按照线程使用情况和职责分成两大类:
Java堆在JVM启动时创建内存区域去实现对象、数组与运行时常量的内存分配,它是虚拟机管理最大的区域,也是垃圾回收的主要内存区域。
内存划分:
核心逻辑就是三大假说,基于程序运行情况进行不断地优化设计。
**分代收集理论:当前商业虚拟机的垃圾收集器,大多数都遵循了“分代收集”(Generational Collection)的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,**它建立在两个分代假说之上:
这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。
这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用。
JDK 1.7内存模型如下图:
JDK 1.8内存模型如下图:
JDK 1.9内存模型如下图:
栈帧(Stack Frame)是用于支持虚拟机进行方法执行的数据结构。
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程。
栈内存为线程私有的空间,每个线程都会创建私有的栈内存,生命周期与线程相同,每个Java方法在执行的时候都会创建一个栈帧(Stack Frame)。栈内存大小决定了方法调用的深度,核内存过小则会导致方法调用的深度较小,如递归调用的次数较少。
虚拟机栈的构成如下:
一个线程中方法的调用链可能会很长,所以会有很多栈帧。只有位于JVM虚拟机栈栈顶的元素才是有效的,即称为当前栈帧,与这个栈帧相关连的方法称为当前方法,定义这个方法的类叫做当前类。
执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。如果当前方法调用了其他方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧了。
调用新的方法时,新的栈帧也会随之创建。并且随着程序控制权转移到新方法,新的栈帧成为了当前栈帧。方法返回之际,原栈帧会返回方法的执行结果给之前的栈帧(返回给方法调用者),随后虚拟机将会丢弃此栈帧。
本地方法栈和虚拟机栈相似,区别就是虚拟机栈为虚拟机执行Java服务(字节码服务),而本地方法栈为虚拟机使用到的Native方法(比如C++方法)服务。
简单地讲,一个Native Method就是一个Java调用非Java代码的接口。
方法区(Method Area)是可供各个线程共享的运行时内存区域,方法区本质上是Java语言编译后代码存储区域,它存储每一个类的结构信息,例如:运行时常量池、成员变量、方法数据、构造方法和普通方法的字节码指令等内容。很多语言都有类似区域。
方法区的具体实现有两种:永久代(PermGen)、元空间(Metaspace)。
方法区构成如下:
主要有如下三种类型:
如果需要方法方法区中类的其他信息,都必须先获得Class对象,才能去方法该Class对象关联的方法信息或者字段信息。
方法区实现变迁历史如下图:
单独使用""引号创建的字符串都是常量,编译期就已经确定存储到Sstring Pool中。
使用new String(“”)创建的对象会存储到heap中,是运行期新创建的。
使用只包含常量的字符串连接符如"aa"+"bb"创建的也是常量,编译期就能确定已经存储到StringPool中。
使用包含变量的字符串连接如"aa"+s创建的对象是运行期才创建的,存储到heap中。
运行期调用String的intern()方法可以向String Pool中动态添加对象。
程序计数器(Program Counter Register),也叫PC寄存器,是一块较小的内存空间,它可以看作是**当前线程所执行的字节码指令的行号指示器。**字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支,循环,跳转,异常处理,线程回复等都需要依赖这个计数器来完成。
由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(针对多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换(系统上下文切换)后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存。
如果一个线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个Native方法,这个计数器的值则为空。
异常:此内存区域是唯一一个在Java的虚拟机规范中没有规定任何OutOfMemoryError异常情况的区域。
直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。在JDK1.4中新加入了NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
直接内存(堆外内存)与堆内存比较:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。