目录
- 问题背景
- 答案其实不是固定的
- JDK 8之前的情况
- JDK 8之后的变化
- 为什么会有这个变化?
- 实际验证
- 不同存储区域的内容
- 面试时怎么回答?
- 总结
问题背景
最近在复习Java基础时,遇到了一个很常见的问题:静态变量到底存储在哪里?网上的答案各不相同,有的说在方法区,有的说在堆内存。今天就来整理一下这个问题。
答案其实不是固定的
JDK 8之前的情况
在JDK 8之前,静态变量确实是存储在方法区的。那时候方法区的具体实现叫做永久代(PermGen)。
public class Example { private static int upXTTIIqXcount = 0; // 存储在永久代 private static String name = "test"; // 存储在永久代 private int age = 18; // 实例变量,存储在堆 }
这个时候说"静态变量在方法区"是正确的。
JDK 8之后的变化
从JDK 8开始,oracle对JVM做了一个重要改动:
- 移除了永久代
- 引入了元空间(MetASPace),使用本地内存
- 静态变量被移到了堆内存中
所以在JDK 8及以后的版本中,静态变量实际上是存储在堆内存里的。
public class ModernExample { // 在JDK 8+中,这些静态变量都在堆内存中 private static List<String> list = new ArrayList<>(); private static final int MAX_SIZE = 100; public static void main(String[] args) { // 这些操作的数据都在堆内存中 list.add("hello"); System.out.println(MAX_SIZE); } }
为什么会有这个变化?
主要是因为永久代有一些问题:
- 大小固定:永久代大小在启动时就确定了,容易出现OutOfMemoryError
- 调优困难:需要合理设置永久代大小,但很难准确估算
- GC效率低:永久代的垃圾回收效率不高
元空间使用本地内存,可以动态扩展,解决了这些问题。
实际验证
我们可以通过一个简单的程序来观察:
public class MemoryTest { private static byte[] staticArray = new byte[1024 * 1024]; // 1MB android public static void main(String[] args) { // 使用 -XX:+PrintGCDetails 可以观察内存分配情况 System.out.println("Static array created"); // 创建一些对象触发GC for (int i = 0; i < 100; i++) { byte[] temp = new byte[1024 * 1024]; // 1MB } } }
运行时加上参数:-XX:+PrintGCDethttp://www.devze.comails -Xmx100m
在JDK 8+中,你会发现静态数组占用的是堆内存空间。
从实际的GC日志可以看到:
[0.088s][info][gc,heap] GC(0) Eden regions: 3->0(15) [0.088s][info][gc,heap] GC(0) Survivor regions: 0->1(3) [0.088s][info][gc,heap] GC(0) Old regions: 0->0 [0.088s][info][gc,heap] GC(0) Humongous regions: 44->2
关键信息分析:
Humongous regions: 44->2 : 这javascript里的44个大对象区域就包含了我们的静态数组
堆内存总使用情况 :46M->2M(100M) 表示GC前后堆内存的变化
元空间单独统计 : Metaspace: 501K(704K)->501K(704K) 元空间的使用情况单独记录
证明静态变量在堆的证据:
静态数组(1MB)被分配在Humongous regions中,这是G1垃圾收集器堆内存的一部分
如果静态变量在元空间,那么元空间的使用量应该会显著增加,但实际上元空间只有几百KB
GC日志中堆内存的变化包含了静态变量的内存占用
不同存储区域的内容
现在的JDK 8+版本中:
堆内android存中存储:
- 对象实例
- 实例变量
- 静态变量
元空间中存储:
- 类的元数据信息
- 方法信息
- 常量池中的符号引用
程序计数器、虚拟机栈、本地方法栈:
- 方法执行时的局部变量
- 方法调用信息
面试时怎么回答?
如果面试官问这个问题,比较好的回答方式是:
这个问题需要区分JDK版本。在JDK 8之前,静态变量存储在方法区的永久代中。从JDK 8开始,移除了永久代,引入了元空间,同时将静态变量移到了堆内存中。所以在现在常用的JDK 8及以后版本中,静态变量是存储在堆内存里的。
总结
- JDK 8之前:静态变量在方法区(永久代)
- JDK 8及之后:静态变量在堆内存
到此这篇关于Java中静态变量是在堆还是方法区的文章就介绍到这了,更多相关Java静态变量是在堆还是方法区内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论