RuntimeDataArea

#GC #JAVA #JVM #Heap #Stack #Runtime Data Area

Java Virtual Machine Stacks

是一块内存区域,用于存放存放单个线程的数据如方法和局部变量,一个Stack里有多个Stack Frame.


  • 更大的栈内存设置不会提高效率,会减少最大同时运行线程数量
  • 栈内存可固定也可以动态变化
  • static变量可以被多个线程同时调用,需要考虑线程安全
  • 私有局部变量只存在在该栈内只能被这个栈调用,通常认为是安全的。
  • 当局部变量发生Escape时,不再线程安全,如参数传递,返回值泄露
  • 即使没有递归也有可能发生StackOverFlow,比如循环引用.

Heap

最大的一块内存空间,被所有线程共享,存放着大部分的对象实例,就是new出来的对象.


Heap分为三大类,新生区,老年区和元空间(永久代).

Young Generation

新生代由Eden和两组Survivor空间组成,通常eden要远大于Survivor区域,而Survivor中s0 : s1 == 1 : 1


  • 当有新的对象加入 Heap 时通常首先进入 Eden.
  • 当发生 [[GC(Garbage Collectors)#^Young GC|Young GC]] 时Eden尝试会将Eden和 s1/s0 中全部对象移入s0或s1,并给这些对象年龄加1
  • 当对象进行多次 GC 后年龄达到阈值会被移入** Old Generation **
  • 如果新加入的对象过大会直接进入 Old Generation
  • Survivor Space的 from 和 to 指的是在复制整理对象时的原位置 和 复制位置.

Old Generation

老年代存放着大对象和长期存活的对象


大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。

大对象直接进入老年代的行为是由虚拟机动态决定的,它与具体使用的垃圾回收器和相关参数有关。大对象直接进入老年代是一种优化策略,旨在避免将大对象放入新生代,从而减少新生代的垃圾回收频率和成本。

当对象经历过多次 Young GC 依旧存活达到阈值(通常是15/8) 将被移入 Old Generation.

Method Area

是JVM运行时内存结构中的一块 区域, 被所有JVM线程共享, 存储每个类的结构信息.


run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and interface initialization and in instance initialization.

StirngTable

StringTable(String Pool) 在Java7之前存放在 ConstantPool,之后Method Area移动到本地内存实现,但是 StringTable 依旧在堆中.

  • String 存放在 StringTable中,是不可变对象,能够被多个对象引用.
String s1 = "a";
String s2 = "b";

String s3 = s1 + s2; 
//StirngBuilder().append(s1).append.(s2).toString(); new String("ab");

String s4 = "ab"

// s3 != s4

String s5 = "a" + "b"
//s4 == s5

因为s3 相当于new了一个新对象存储,所以存储在堆中.

而s4则是存储在 StringPool 当中.

两者指向的对象不同, 而 == 是比较的引用地址,所以flase

如果使用.equals() 就相同.

在拼接s5时编译器进行优化,直接从StringPool找到 a b 进行拼接,发现结果是已有的 ab,

于是直接指向ab.

intern方法 1.8

调用字符串对象的 intern 方法,会将当前**堆中字符串对象的引用注册**到串池中

  1. 检查字符串常量池中是否已存在该字符串
  2. 如果不存在,则将当前堆中字符串对象的引用注册到字符串常量池中
  3. 如果存在,直接返回池中的引用,不更新引用.
public class Main {
	public static void main(String[] args) {
		// "a" "b" 被放入串池中,str 则存在于堆内存之中
		String str = new String("a") + new String("b");
		// 调用 str 的 intern 方法,这时串池中没有 "ab" ,则会将堆中字符串对象的引用注册到串池,此时堆内存与串池中的 "ab" 是同一个对象
		String st2 = str.intern();
		// 给 str3 赋值,因为此时串池中已有 "ab" ,则直接将串池中的内容返回
		String str3 = "ab";
		// 因为堆内存与串池中的 "ab" 是同一个对象,所以以下两条语句打印的都为 true
		System.out.println(str == st2);
		System.out.println(str == str3);
	}
}

Native Method Stack

PC Register

Escape
public static StringBuffer craeteStringBuffer(String s1, String s2) {
   StringBuffer sb = new StringBuffer();
   sb.append(s1);
   sb.append(s2);
   return sb;
}
  • 创建的局部变量 sb 通过return方法传递,将sb脱离了自己掌控,线程逃逸.

来源

JVM 基础 - JVM 内存结构

21_堆_内存诊断_jvirsualvm_哔哩哔哩_bilibili

Chapter 2. The Structure of the Java Virtual Machine

JVM垃圾回收详解(重点)