转载

JAVA-大白话探索JVM-运行时内存(三)

  1. 类的结构信息和类静态变量都保存在方法区(这样说会不会很抽象,举个例,例如运行时常量池,成员变量和方法数据,构造函数和普通函数的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。开发人员在程序中通过Class对象中的getName、isInstance等方法获取信息时,这些数据都来自方法区。)
  2. 程序中的所有线程共享一个方法区,简称全局共享
  3. 对于HotSpot虚拟机,方法区对应为永久代(Permanent Generation),但本质上,两者并不等价,仅仅是因为HotSpot虚拟机的设计团队是用永久代来实现方法区而已,对于其他的虚拟机(JRockit、J9)来说,是不存在永久代这一概念的。
  4. 使用永久代来实现方法区并不是一个好注意,由于方法区会存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等,在某些场景下非常容易出现永久代内存溢出。如Spring、Hibernate等框架在对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载入内存。在JSP页面较多的情况下,也会出现同样的问题。
  5. 在JDK1.8下并没有出现我们期望的永久代内存溢出错误,而是Metaspace内存溢出错误。这是因为Java团队从JDK1.7开始就逐渐移除了永久代,到JDK1.8时,永久代已经被Metaspace取代,因此在JDK1.8并没有出现我们期望的永久代内存溢出错误。在JDK1.8中,JVM参数-XX:PermSize和-XX:MaxPermSize已经失效,取而代之的是-XX:MetaspaceSize和XX:MaxMetaspaceSize。注意:Metaspace已经不再使用堆空间,转而使用Native Memory
  6. 还有一点需要说明的是,在JDK1.6中,方法区虽然被称为永久代,但并不意味着这些对象真的能够永久存在了,JVM的内存回收机制,仍然会对这一块区域进行扫描,即使回收这部分内存的条件相当苛刻。

呃。。。。。。。有点多,慢慢吸收,这方法区也需要好好琢磨琢磨,一不小心溢出就麻烦了。

其次,Java堆(Heap,线程共享)

  1. Java堆是JVM所管理的最大一块内存,所有线程共享这块内存区域,几乎所有的对象实例都在这里分配内存,因此,它也是垃圾收集器管理的主要区域。
  2. 从内存回收的角度来看,由于现在的收集器基本都采用分代收集算法,所以Java堆又可以细分成:新生代和老年代,新生代里面有分为:Eden空间、From Survivor空间、To Survivor空间。
  3. 有一点需要注意:Java堆空间只是在逻辑上是连续的,在物理上并不一定是连续的内存空间。
  4. 默认情况下,新生代中Eden空间与Survivor空间的比例是8:1,可以使用参数-XX:SurvivorRatio对其进行配置。大多数情况下,新生对象在新生代Eden区中分配,当Eden区没有足够的空间进行分配时,则触发一次Minor GC,将对象Copy到Survivor区,如果Survivor区没有足够的空间来容纳,则会通过分配担保机制提前转移到老年代去。
  5. 何为分配担保机制?在发送Minor GC前,JVM会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果是,那么可以确保Minor GC是安全的,如果不是,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果小于,直接进行Full GC,如果大于,将尝试着进行一次Minor GC,Minor GC失败才会触发Full GC。注:不同版本的JDK,流程略有不同。
  6. Survivor区作为Eden区和老年代的缓冲区域,常规情况下,在Survivor区的对象经过若干次垃圾回收仍然存活的话,才会被转移到老年代。JVM通过这种方式,将大部分命短的对象放在一起,将少数命长的对象放在一起,分别采取不同的回收策略。

Java栈(Stack,线程私有)、本地方法栈

Java栈

  1. java栈中只保存基础数据类型(四类八种)和自定义对象引用
  2. 存取类型:先进后出
  3. 栈内数据在超出其作用域将自动释放
  4. 每个栈是线程私有,它们的生命周期与线程相同。
  5. 每个线程建立一个操作栈,每个栈又包含若干个栈帧,每个栈帧对应每个方法调用
  6. 栈帧:
    • 局部变量区(方法内基本类型变量、变量对象指针)
    • 操作数栈区(存放方法执行过程中产生的结果)
    • 运行环境区(动态链接、方法返回相关信息、异常捕捉)

本地方法栈

  1. 与JAVA栈类似
  2. 本地方法栈是在程序调用或JVM调用本地方法接口(Native)时候启用
  3. 本地方法非java语言编写,不受JVM管理
  4. HotSpot VM将本地方法栈和JVM栈合并了。

程序计数器(线程私有)

概念:在JVM概念模型里,字节码解释器工作时就说通过改变这个计算器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

  1. Java虚拟机可以支持多条线程同时执行,多线程是通过线程轮流切换来获得CPU执行时间的,每条线程都会有独立的程序计数器
  2. 如果执行java方法,程序计数器记录JVM字节码指令的地址,如果执行 native,计数器为空(Underfined)
  3. 程序计数器这个内存区域在JVM规范中是唯一没有规定任何OutOfMemoryError的区域
原文  https://juejin.im/post/5c5015fff265da616d548e2c
正文到此结束
Loading...