1. 对象大量创建且无法被回收
场景:程序在短时间内大量创建对象,但这些对象无法及时被垃圾回收器回收,最终导致堆内存耗尽。
原因:
- 大量对象创建。
- 对象被某些集合(如List、Map)持有强引用,无法被GC回收。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class MemoryLeakExample {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object());
}
}
}
2. 未关闭的资源
场景:在处理文件或网络连接时,未及时关闭输入输出流或连接,导致内存泄漏。
原因:
- 未关闭的流或连接会持续占用内存。
- 无法被GC回收,逐渐耗尽内存资源。
示例代码:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ResourceLeakExample {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader("largefile.txt"));
while (reader.readLine() != null) {
// Process the line
}
// Missing reader.close(); here
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 静态变量持有对象引用
场景:静态变量持有对象引用,导致这些对象无法被GC回收,逐渐占用大量内存。
原因:
- 静态变量生命周期与应用程序一致,持有对象引用导致这些对象无法回收。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class StaticReferenceMemoryLeakExample {
private static List<Object> staticList = new ArrayList<>();
public static void main(String[] args) {
while (true) {
staticList.add(new Object());
}
}
}
4. 不正确的线程池配置
场景:使用线程池执行任务时,未正确配置线程池大小,导致过多的线程创建,耗尽内存。
原因:
- 无限制地创建新线程。
- 每个线程占用一定的堆内存和本地内存,过多的线程会耗尽内存资源。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolMemoryLeakExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
while (true) {
executor.execute(() -> {
// Simulate task
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
}
5. 大型数据结构未及时清理
场景:使用大型数据结构(如缓存、集合)存储数据,未及时清理,导致内存溢出。
原因:
- 数据结构不断增长,占用大量内存。
- 缓存数据未设定过期时间或清理策略。
示例代码:
import java.util.HashMap;
import java.util.Map;
public class LargeDataStructureMemoryLeakExample {
private static Map<Integer, String> cache = new HashMap<>();
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
cache.put(i, "Some large string data " + i);
}
}
}
6. 内存泄漏由于监听器或回调未释放
场景:注册的事件监听器或回调未及时移除,导致内存泄漏。
原因:
- 注册的监听器或回调对象未移除,持续占用内存。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class ListenerMemoryLeakExample {
private static List<Runnable> listeners = new ArrayList<>();
public static void main(String[] args) {
while (true) {
listeners.add(() -> {
// Simulate event handling
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
}
7. 类加载器泄漏
场景:类加载器未释放,常见于动态加载类或应用服务器热部署场景。
原因:
- 类加载器持有对类和对象的引用,未被释放。
示例代码:
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoaderMemoryLeakExample {
public static void main(String[] args) throws Exception {
while (true) {
URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:/path/to/your/classes/")});
Class<?> clazz = classLoader.loadClass("YourClass");
clazz.newInstance();
}
}
}
8. 循环引用
场景:对象间存在循环引用且未被GC回收。
原因:
- 循环引用的对象无法被GC及时回收,导致内存占用增加。
示例代码:
public class CircularReferenceMemoryLeakExample {
static class Node {
Node next;
}
public static void main(String[] args) {
Node first = new Node();
Node second = new Node();
first.next = second;
second.next = first;
// Hold references
while (true) {
// Simulate workload
}
}
}
9. 大数组分配
场景:分配超大数组,超出堆内存限制。
原因:
- 超大数组分配时,堆内存不足以容纳,导致内存溢出。
示例代码:
public class LargeArrayMemoryLeakExample {
public static void main(String[] args) {
int[] largeArray = new int[Integer.MAX_VALUE];
}
}
10. JNI代码中的内存泄漏
场景:使用JNI调用本地代码,未正确释放本地内存。
原因:
- 本地代码分配的内存未及时释放,导致内存泄漏。
示例代码:
public class JNIMemoryLeakExample {
static {
System.loadLibrary("NativeLib");
}
public static void main(String[] args) {
while (true) {
allocateMemoryInNative();
}
}
private static native void allocateMemoryInNative();
}
11. JVM参数配置不当
场景:堆内存设置过小,无法满足应用需求。
原因:
- JVM堆内存参数设置不合理,应用程序需要更多内存。
示例代码:
// 启动参数
// java -Xmx64m -jar YourApplication.jar
public class JVMParameterMemoryLeakExample {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1 * 1024 * 1024]); // 1MB
}
}
}
12. 内存泄漏由于不正确的对象池管理
场景:对象池管理不当,导致对象无法被回收。
原因:
- 对象池中的对象未及时释放,导致内存占用增加。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class ObjectPoolMemoryLeakExample {
private static List<Object> objectPool = new ArrayList<>();
public static void main(String[] args) {
while (true) {
objectPool.add(new Object());
}
}
}
13. 第三方库的内存泄漏
场景:使用的第三方库有内存泄漏问题。
原因:
- 第三方库中的对象未及时释放,导致内存泄漏。
示例代码:
import some.third.party.Library;
public class ThirdPartyMemoryLeakExample {
public static void main(String[] args) {
while (true) {
Library.someMethod();
}
}
}
14. String常量池导致的内存泄漏
场景:过多的字符串常量进入常量池,导致内存耗尽。
原因:
- 字符串常量池无限增长,占用大量内存。
示例代码:
public class StringPoolMemoryLeakExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
15. 过多的线程本地变量(ThreadLocal)
场景:使用ThreadLocal变量未及时清理,导致内存泄漏。
原因:
- ThreadLocal变量未及时清理,导致线程结束后仍持有对象引用,无法被GC回收。
示例代码:
public class ThreadLocalMemoryLeakExample {
private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
while (true) {
Thread thread = new Thread(() -> {
threadLocal.set(new byte[1024 * 1024]); // 1MB per thread
// Do some work
});
thread.start();
}
}
}
16. 持久化缓存(如EHCache)未正确配置
场景:持久化缓存未配置合适的清理策略,导致内存占用过多。
原因:
- 缓存对象未设定过期策略或未及时清理,导致内存占用持续增加。
示例代码:
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
public class EHCacheMemoryLeakExample {
public static void main(String[] args) {
CacheManager cacheManager = CacheManager.create();
Cache cache = new Cache("myCache", 1000, false, false, 0, 0);
cacheManager.addCache(cache);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
cache.put(new Element(i, new byte[1024 * 1024])); // 1MB per entry
}
}
}
17. 反射导致的内存泄漏
场景:使用反射创建对象未及时释放,导致内存泄漏。
原因:
- 反射创建的对象未被及时清理,持有对Class对象的引用,导致内存泄漏。
示例代码:
import java.lang.reflect.Method;
public class ReflectionMemoryLeakExample {
public static void main(String[] args) throws Exception {
while (true) {
Class<?> clazz = Class.forName("java.util.Date");
Method method = clazz.getMethod("toString");
method.invoke(clazz.newInstance());
}
}
}
18. 持续增长的日志
场景:内存日志不断增加,未及时清理或持久化,导致内存占用过多。
原因:
- 内存中记录的日志信息不断增加,未及时清理或写入磁盘,导致内存占用持续增加。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class LoggingMemoryLeakExample {
private static List<String> logs = new ArrayList<>();
public static void main(String[] args) {
while (true) {
logs.add("Log entry " + System.currentTimeMillis());
// Simulate processing
}
}
}
19. 长时间运行的应用(如服务器)
场景:长时间运行未进行内存优化或重启,导致内存逐渐耗尽。
原因:
- 长时间运行过程中,未进行内存优化或重启,导致内存碎片化或积累,最终耗尽内存。
示例代码:
public class LongRunningApplicationMemoryLeakExample {
private static List<byte[]> memoryHog = new ArrayList<>();
public static void main(String[] args) {
while (true) {
memoryHog.add(new byte[1024 * 1024]); // 1MB per iteration
// Simulate long-running process
}
}
}
20. 大数据处理任务
场景:处理超出内存限制的大数据集,导致内存溢出。
原因:
- 大数据集处理时,需要的内存超过JVM堆内存限制,导致内存溢出。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class BigDataMemoryLeakExample {
public static void main(String[] args) {
List<int[]> bigData = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
bigData.add(new int[1000000]); // Large arrays
}
}
}
通过分析和优化这些可能导致OOM的情况,可以有效防止Java应用内存溢出。工具如jvisualvm、jmap、jhat等可以帮助检测和分析内存泄漏。