生产OOM排查

简化版代码:

package com.mystic.ycc.blog.test;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class TestOOM {

    /**
     * @param args
     */
    public static void main(String[] args) {
       // testAESOOM();
        testMap();
    }

    /**
     *      jvm 参数:-Xmx2048m -Xms512m  -XX:+HeapDumpOnOutOfMemoryError   -XX:HeapDumpPath=file.hprof
     *      打开JVISUALVM 可以看到JDK的自调整(没有配置其他参数的情况下)
     */
    public static void testMap(){
                Map<Integer, HashSet<String>> masterMap = new HashMap<>(100000);
        for (int i = 0; i < 100000; i++) {
            System.out.println(i);
            masterMap.computeIfAbsent(i, HashSet::new).add("test"+i);
        }
    }

    /**
     *jvm 参数:-Xmx512m -Xms512m 避免jdk自调整  -XX:+HeapDumpOnOutOfMemoryError   -XX:HeapDumpPath=file.hprof
     * <dependency>
     * <groupId>org.bouncycastle</groupId>
     * <artifactId>bcprov-jdk15on</artifactId>
     * <version>1.70</version>
     * </dependency>
     */
    public static void testAESOOM(){

        try {
            String key = "1234567890123456"; // 16字节密钥
            String iv = "abcdefghijklmnop";  // 16字节IV
            String plainText = "Hello, BouncyCastle!";
            for (int i = 0; i < 100000; i++) {
                // 加密
                String encryptedText = encrypt(plainText, key, iv);
                System.out.println("加密后的数据: " + encryptedText);
                // 解密
                String decryptedText = decrypt(encryptedText, key, iv);
                System.out.println("解密后的数据: " + decryptedText);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // AES加密模式为CBC,填充方式为PKCS5Padding
    private static final String AES_CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";

    // 密钥和IV(初始化向量)的长度必须为16字节(128位)
    private static final int KEY_LENGTH = 16;
    private static final int IV_LENGTH = 16;

    /**
     * AES加密
     * @param plainText 明文
     * @param key       密钥
     * @param iv        初始化向量
     * @return 加密后的Base64编码字符串
     * @throws Exception 加密过程中的异常
     */
    public static String encrypt(String plainText, String key, String iv) throws Exception {
        // 检查密钥和IV的长度
        if (key.length() != KEY_LENGTH || iv.length() != IV_LENGTH) {
            throw new IllegalArgumentException("密钥和IV的长度必须为16字节(128位)");
        }

        // 创建AES密钥
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");

        // 创建初始化向量
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
        BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
        // 创建Cipher对象并初始化为加密模式
        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING, bouncyCastleProvider); // 使用BouncyCastle Provider
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

        // 加密数据
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

        // 返回Base64编码的加密数据
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * AES解密
     * @param encryptedText 加密的Base64编码字符串
     * @param key          密钥
     * @param iv           初始化向量
     * @return 解密后的明文
     * @throws Exception 解密过程中的异常
     */
    public static String decrypt(String encryptedText, String key, String iv) throws Exception {
        // 检查密钥和IV的长度
        if (key.length() != KEY_LENGTH || iv.length() != IV_LENGTH) {
            throw new IllegalArgumentException("密钥和IV的长度必须为16字节(128位)");
        }

        // 创建AES密钥
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");

        // 创建初始化向量
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
        BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
        // 创建Cipher对象并初始化为解密模式
        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING, bouncyCastleProvider); // 使用BouncyCastle Provider
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);

        // 解码Base64并解密数据
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));

        // 返回解密后的明文
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

}

排查思路

  • 使用工具 :IDEA,IDEA_Profile插件,JVM参数
  • 必须配置参数:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=file.hprof
  • JProfile工具

生产OOM排查

生产OOM排查

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
javaJDKjvm

线程和同步性能

2025-3-14 9:20:56

javaJDKjvm

Java 生态下想搞大流量下的 ws,是不是暂时只能 netty?

2025-3-21 8:52:18

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧