【JVM】Java内存管理神器:深入理解堆和方法区

478 阅读4分钟

关于堆和方法区浅析

JVM结构

image.png

新建对象语句一目了然,废话不多说,看图

image.png

image.png

一、堆(Heap)

【概述】

  • Java内存区域中一块用来存放对象实例的区域几乎所有的对象实例都在这里分配内存】 通过new关键字创建的对象都会被放在堆内存,jvm 运行时数据区中,占用内存最大的就是堆(Heap)内存!因此,它是垃圾收集器(GC)管理的主要目标

  • Java堆区在JVM启动的时候即被创建,其空间大小也就被确定了,几乎所有的对象实例以及数组都在运行时分配到堆上,所以它是JVM管理的最大的一块、也是最重要的一块内存空间。在方法结束后,堆中的对象不回马上被移除,仅仅在垃圾收集的时候才被移除,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。

  • 而对象实例和数组可能永远不会存储在栈上,因为栈帧中保存的是引用,这个引用指向对象实例或者数组在堆中的位置。

image.png

堆可以处于物理上不连续的内存空间,但在逻辑上它应该被视为连续的。

【堆空间大小的设置和查看】

默认情况下:

  • 初始内存大小:物理电脑内存大小 1/ 64
  • 最大内存大小:物理电脑内存大小 1/ 4 Edit Configration>add VM option>输入
  • -Xms:可设置堆的初始空间,等价于-XX:InitialHeapSize
  • -Xmx:可设置堆的最大空间,等价于-XX:MaxHeapSize
  • -XX:+PrintGCDetail:可查看堆空间中设置的参数
  • -Xmn:可设置年轻代的空间大小

image.png

  • 在HotSpot中,Eden(伊甸园)空间和另外两个Survivor空间缺省所占的比例是8:1:1,当然我们可以通过选项"-XX:SurvivorRatio"调整这个比例空间。 实际情况中,当我们查看内存分配是时发现并不是8:1:1,是因为系统为我们默认定义了自适应内存分配策略,我们可以通过"-XX:-UseAdaptiveSizePolicy"关闭自适应的内存分配策略。

二、方法区

  • 《Java虚拟机规范》中说明:“尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩”。但对于HotSpot JVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。所以,方法区可以看作是独立于Java堆的内存空间。
  • 另外,方法区包含了一个特殊的区域“运行时常量池”。
  • 其中主要存储加载的类字节码、class/method/field等元数据对象、static-final常量、static变量、jit编译器编译后的代码等数据。

image.png

  • 类型信息:( 类class、接口interface、枚举enum、注解annotation)JVM必须在方法区中存储以下类型信息:

    • 这个类型的完整有效名称(全名=包名.类名)
    • 这个类型直接父类的完整有效名(对于interface或是java. lang.Object,都没有父类)
    • 这个类型的修饰符(public, abstract, final的某个子集)
    • 这个类型直接接口的一个有序列表
  • 域信息(成员变量):

    • JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。
    • 域的相关信息包括:域名称、 域类型、域修饰符(public, private, protected, static, final, volatile, transient的某个子集)。
  • 方法信息:JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序

    • 方法名称

    • 方法的返回类型(或void)

    • 方法参数的数量和类型(按顺序)

    • 方法的修饰符(public, private, protected, static, final,synchronized, native , abstract的一个子集)

    • 方法的字节码(bytecodes)、操作数栈、局部变量表及大小( abstract和native 方法除外)异常表( abstract和native方法除外)

    • 每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引

image.png

方法区的演进:面试常问

  • Jdk 1.6 及之前:有永久代(静态变量存放在永久代上)、字符串常量池(1.6在方法区)
  • Jdk 1.7 :有永久代,但已经逐步 " 去永久代 ",字符串常量池、静态变量移除,保存在堆中
  • dk 1.8 及之后: 无永久代,常量池1.8在元空间。但静态变量、字符串常量池仍在堆中

参考