JVM入门之内存结构(堆、方法区)
JVM是Java虚拟机的缩写,是Java技术的核心和基础。学习JVM内存结构对于Java程序员来说非常重要,本文将对JVM内存结构、堆和方法区进行详细讲解。
JVM内存结构
JVM的内存结构主要由以下几个部分组成:
- 程序计数器
- 虚拟机栈
- 本地方法栈
- 堆
- 方法区
其中堆和方法区是Java程序中数据存储的主要区域,我们重点来详细介绍。
堆
堆是Java虚拟机中内存最大的一块,所有类的实例和数组都在堆上分配空间。堆是线程共享的,所有线程都可以访问并修改堆中的对象。
Java中,堆内存的大小是通过-Xmx和-Xms指定的,-Xmx指定堆内存的最大容量,-Xms指定堆内存的初始容量。
堆的分类
Java堆分为新生代和老年代两部分,新生代又分为Eden空间、Survivor1空间、Survivor2空间(也叫From空间和To空间)。
-
新生代:是所有新创建对象的存储空间,占堆空间的一部分,一般为1/3。新生代的大小可以通过-Xmn来指定,默认为整个堆的1/3。
-
老年代:是存储长期存活的对象的空间,占堆空间的2/3。老年代的大小可以通过-XX:PermSize和-XX:MaxPermSize来指定。
堆的垃圾回收
Java中的垃圾回收主要是针对堆中的对象的回收。垃圾回收的目的是为了使Java程序能够自动释放不再被使用的内存空间。
Java的垃圾回收算法主要包括三种:
- 标记-清除算法
- 复制算法
- 标记-整理算法
其中复制算法一般用于新生代内存的垃圾回收,而标记-清除算法和标记-整理算法一般用于老年代内存的垃圾回收。
方法区
方法区是用于存储类的相关信息,比如类的成员变量、方法信息、静态变量、运行时常量池等等。方法区的大小由-XX:PermSize和-XX:MaxPermSize参数指定,默认值为64M。
方法区的分类
方法区分为两个部分:常量池和类信息。
- 常量池:Class文件中的常量池用于存放编译期生成的各种字面量和符号引用。
- 类信息:存放每个类的结构信息,比如成员变量、方法等信息。
方法区的垃圾回收
方法区的垃圾回收主要针对无用的类和常量的回收。Java7及之前的版本中,方法区的垃圾回收主要依靠对类的卸载实现。Java8开始,方法区的垃圾回收采用了元空间的概念进行实现。元空间直接使用本地内存,因此不存在垃圾回收问题。
示例说明
示例一
public class Demo1 {
public static void main(String[] args) {
byte[] b1 = new byte[1024 * 1024];
System.out.println("分配了1M空间给b1");
byte[] b2 = new byte[1024 * 1024 * 2];
System.out.println("分配了2M空间给b2");
byte[] b3 = new byte[1024 * 1024 * 3];
System.out.println("分配了3M空间给b3");
byte[] b4 = new byte[1024 * 1024 * 4];
System.out.println("分配了4M空间给b4");
}
}
这个示例中分别分配了1M、2M、3M、4M的空间给b1、b2、b3、b4。由于堆大小为5M(默认为整个堆的1/3),因此最后一次分配空间的时候会出现OutOfMemoryError错误。
示例二
public class Demo2 {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = "abc";
String s3 = "ab" + "c";
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // false
System.out.println(s2 == s3); // true
}
}
这个示例中,我们分别使用new关键字和直接赋值的方式创建了三个字符串对象s1、s2、s3。Java的字符串常量会被JVM缓存,因此s2和s3引用的是同一个字符串常量对象。而s1由于使用了new关键字创建,因此它引用的是一个新的字符串对象,与s2和s3不同。因此s1、s2、s3三个变量的比较结果也是不同的。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JVM入门之内存结构(堆、方法区) - Python技术站