jvm详解

大多数 JVM 将内存区域划分:


Method Area(Non-Heap)(方法区) ——线程共享


Heap(堆) ——线程共享


Program Counter Register(程序计数器) ——非线程共享


VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的)——非线程共享


Native Method Stack ( 本地方法栈 )——非线程共享


JVM运行的时候会分配好 Method Area(方法区) 和Heap(堆),而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)和Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。 
非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行生命周期相同, 
所以gc只发生在线程共享的区域(大部分发生在Heap上)的原因。 

线程共享: 
一、方法区
有时候也称为永久代(Permanent Generation),在该区内很少发生垃圾回收,在这里进行的GC主要是方法区里的常量池和类型的卸载
方法区主要用来存储已被虚拟机加载的类信息、常量、静态变量和即时编译后的代码等数据。
方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。运行时生成的常量也会存在这个常量池中。比如String类的intern()方法

    总结:

        

    保存装载的类信息

  类型的常量池

  字段,方法信息

  方法字节码

通常和永久区(Perm)关联在一起


二、堆 
在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。 
堆空间分为老年代和年轻代。刚创建的对象存放在年轻代,而老年代中存放生命周期长久的实例对象。年轻代中又被分为Eden区(圣经中的伊甸园)、和两个Survivor区(From Space和To Space)。新的对象分配是首先放在Eden区,Survivor区作为Eden区和Old区的缓冲,在Survivor区的对象经历若干次收集仍然存活的,就会被转移到老年代。 
这里写图片描述
堆中垃圾回收的时候注意一个大对象回收过程,描述如下: 
年轻代与老年代控件分配如下:


Eden:80M


S1:10M


S2:10M


老年代:100M


过程:

新建60M的对象O1,此时Eden有足够内存吃下,所以60M被分配到Eden区。
再新建40M对象O2,此时因为S1和S2的控件不足以容纳下O1,所以O1被直接分配到老年代,而O2进入Eden区
清空O1、O2,新建90M对象O3,发现Eden不足以放下O3,O3被直接放入老年代中
清空O3,新建110M对象O4,O4发现Eden和老年代都没有足够控件,直接返回OutOfMemoryError
结论:
当一个对象大于eden区而小于old区(老年代)时的时候会直接扔到old区。 
而但对象大于old区时,会直接抛出OutOfMemoryError(OOM)。
    

    和程序开发密切相关

    应用系统对象都保存在Java堆中

    所有线程共享Java堆

    对分代GC来说,堆也是分代的

    GC管理的主要区域





线程私有: 
一、虚拟机栈:


虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
局部变量表里存储的是基本数据类型和对象引用。局部变量所需的内存空间在编译器间确定
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用。

总结:

    

  • 线程私有,生命周期和线程相同
  • 栈由一系列帧组成(因此Java栈也叫做帧栈)
  • 帧保存一个方法的局部变量、操作数栈、常量池指针
  • 每一次方法调用创建一个帧,并压栈


二、本地方法栈 
本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。


三、程序计数器 
代表着当前线程所执行字节码的行号指示器。 
分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。 
程序计数器是唯一一个java虚拟机规范没有规定任何OOM(Out Of Memory)情况的区域。

猜你喜欢

转载自blog.csdn.net/wzqllwy/article/details/80262366