以下是针对不同内存区域的溢出模拟程序、解决方案及预防措施:
import java.util.ArrayList;
import java.util.List;
public class HeapOOMSimulator {
public static void main(String[] args) {
List<byte[]> memoryHog = new ArrayList<>();
try {
while (true) {
// 每次分配 10MB
memoryHog.add(new byte[10 * 1024 * 1024]);
Thread.sleep(100); // 模拟业务处理
}
} catch (OutOfMemoryError | InterruptedException e) {
System.err.println("堆内存溢出: " + e.toString());
// 此处应记录日志并安全退出
System.exit(1);
}
}
}
java -Xmx2g -XX:+HeapDumpOnOutOfMemoryError -jar your-app.jar
SparseArray
替代 HashMap
)// 反例:静态集合导致内存泄漏
public class Cache {
private static final Map<Long, Object> CACHE = new HashMap<>();
public void addToCache(Long id, Object obj) {
CACHE.put(id, obj); // 对象永不释放!
}
}
// 正例:使用弱引用缓存
public class SafeCache {
private static final Map<Long, WeakReference<Object>> CACHE = new WeakHashMap<>();
}
import javassist.ClassPool;
public class MetaspaceOOMSimulator {
public static void main(String[] args) {
ClassPool pool = ClassPool.getDefault();
for (int i = 0; ; i++) {
try {
// 动态生成类
pool.makeClass("com.demo.GeneratedClass" + i).toClass();
} catch (Throwable e) {
System.err.println("元空间溢出: " + e.toString());
System.exit(1);
}
}
}
}
java -XX:MaxMetaspaceSize=256m ...
-verbose:class
监控类加载// 反例:频繁创建动态代理
public Object createProxy() {
return Proxy.newProxyInstance( // 每次调用都生成新类
getClass().getClassLoader(),
new Class<?>[]{Service.class},
new InvocationHandler() { ... }
);
}
// 正例:缓存代理实例
public class ProxyFactory {
private static final Map<Class<?>, Object> PROXY_CACHE = new ConcurrentHashMap<>();
public Object getProxy(Class<?> clazz) {
return PROXY_CACHE.computeIfAbsent(clazz, k ->
Proxy.newProxyInstance(...)
);
}
}
import java.nio.ByteBuffer;
import java.util.LinkedList;
public class DirectMemoryOOMSimulator {
public static void main(String[] args) {
LinkedList<ByteBuffer> buffers = new LinkedList<>();
try {
while (true) {
// 分配直接内存
buffers.add(ByteBuffer.allocateDirect(10 * 1024 * 1024));
Thread.sleep(50);
}
} catch (Throwable e) {
System.err.println("直接内存溢出: " + e.toString());
System.exit(1);
}
}
}
java -XX:MaxDirectMemorySize=512m ...
PooledByteBufAllocator
)((DirectBuffer) buffer).cleaner().clean()
)// 反例:未释放直接缓冲区
public void processFile(Path path) throws IOException {
try (FileChannel channel = FileChannel.open(path)) {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);
while (channel.read(buffer) > 0) {
buffer.flip();
// 处理数据...
buffer.clear();
}
}
// 缓冲区未释放,直到GC触发!
}
// 正例:使用try-with-resources扩展
public class DirectBuffer implements AutoCloseable {
private final ByteBuffer buffer;
public DirectBuffer(int size) {
this.buffer = ByteBuffer.allocateDirect(size);
}
@Override
public void close() {
Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
if (cleaner != null) cleaner.clean();
}
}
// 使用:
try (DirectBuffer db = new DirectBuffer(1024 * 1024)) {
ByteBuffer buffer = db.getBuffer();
// 操作缓冲区...
}
public class StackOOMSimulator {
// 情况1:递归栈溢出
public static void recursiveCall(int depth) {
if (depth % 1000 == 0)
System.out.println("Depth: " + depth);
recursiveCall(depth + 1); // 无限递归
}
// 情况2:线程过多
public static void threadOverflow() {
int count = 0;
while (true) {
new Thread(() -> {
try { Thread.sleep(100_000); }
catch (InterruptedException e) {}
}).start();
System.out.println("Thread #" + ++count);
}
}
public static void main(String[] args) {
// 选择一种测试
recursiveCall(0);
// threadOverflow();
}
}
java -Xss2m ... # 增加栈大小
Executors.newFixedThreadPool
)-Xss256k
)// 反例:递归无终止条件
public int calculate(int n) {
if (n == 0) return 1;
return n * calculate(n - 1); // 当n为负数时无限递归
}
// 正例:添加终止条件+迭代法
public int safeCalculate(int n) {
if (n < 0) throw new IllegalArgumentException();
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
// 反例:直接创建线程
public void handleRequests(List<Request> requests) {
for (Request req : requests) {
new Thread(() -> process(req)).start(); // 请求量大时崩溃
}
}
// 正例:使用线程池
private final ExecutorService executor = Executors.newFixedThreadPool(50);
public void safeHandleRequests(List<Request> requests) {
for (Request req : requests) {
executor.submit(() -> process(req));
}
}
java \
-Xms1g -Xmx4g \ # 堆内存
-XX:MaxMetaspaceSize=256m \ # 元空间
-XX:MaxDirectMemorySize=512m \ # 直接内存
-Xss512k \ # 线程栈
-XX:+UseG1GC \ # 垃圾回收器
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/path/to/dumps \
-XX:OnOutOfMemoryError="kill -9 %p" \
-jar your-app.jar
// 添加内存监控
new GarbageCollectorMetrics().bindTo(registry);
new MemoryMetrics().bindTo(registry);
static
集合长期持有对象try-with-resources
管理资源maximumSize()
)// JMH 基准测试示例
@Benchmark
@Fork(value = 1, jvmArgs = "-Xmx256m")
public void testMemoryUsage() {
// 模拟业务操作
}
# Docker 配置示例
docker run -it \
--memory=2g \ # 容器内存限制
--cpus=2 \
-e JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" \
your-java-image
通过以上模拟程序和解决方案,开发人员可以针对不同内存区域实施精准防护,在设计和编码阶段就规避常见内存问题,确保应用稳定性。