有没有这种经历👇:
别急!今天这篇就带你一次搞懂Java内存机制 + 经典错误解析
从JVM的角度,看清你代码中每一个对象的“归宿”。
📌 所有通过 new
关键字创建的对象,统统都存放在这里!
public class HeapDemo {
public static void main(String[] args) {
Object obj = new Object(); // 新对象进堆
List<byte[]> crashList = new ArrayList<>();
while(true) {
crashList.add(new byte[1024*1024]); // 每次分配1MB
}
}
}
🔧 爆点知识:
-Xmx
控制最大堆大小,-Xms
控制初始堆大小每个线程都有自己的栈,函数调用和局部变量都靠它👇
public class StackOverflowDemo {
static void infiniteLoop() {
infiniteLoop(); // 无限递归 = 栈爆炸
}
public static void main(String[] args) {
infiniteLoop(); // StackOverflowError
}
}
🔥 注意:递归过深,就等着被-Xss
限制值拍死!
-XX:MaxMetaspaceSize
(不设置就默认为无限)你以为GC只是个“清道夫”?其实它比你还聪明😎
区域 | 对象特点 | 回收算法 | 频率 |
---|---|---|---|
新生代 | 短命对象 | 复制算法 | 高 |
老年代 | 常驻内存 | 标记-整理 | 低 |
元空间 | 类元数据 | 不回收 | —— |
Object strongRef = new Object(); // 强引用:不回收
SoftReference<Object> softRef = new SoftReference<>(new Object()); // 内存不足时回收
WeakReference<Object> weakRef = new WeakReference<>(new Object()); // GC时回收
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), new ReferenceQueue<>());
📌 强软弱虚引用,写代码要用对位置,否则可能坑到怀疑人生!
static Map<Object, Object> cache = new HashMap<>();
Object key = new Object();
cache.put(key, new byte[1024*1024]);
key = null; // 错误!HashMap还在引用key
✅ 正解:用 WeakHashMap
替代,让GC及时回收!
ExecutorService pool = Executors.newCachedThreadPool();
while(true) {
pool.submit(() -> {
Thread.sleep(1000000);
});
}
💣 无限创建线程 = OOM在招手 ✅ 正确姿势:有界队列 + 合理拒绝策略
String s = new String("abc")
创建几个对象?答:可能是1,也可能是2个(视常量池中是否已有"abc")
答:复写finalize()
方法并让对象自我复活。但千万别在生产用,这是老底黑魔法🙅♂️
工具名 | 用途 |
---|---|
VisualVM | 实时监控内存 |
MAT | 内存泄漏分析利器 |
Arthas | 在线排查全能工具 |
GC日志 | 分析GC开销 |
Jstat | 命令行动态监控 |