import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * 第9课教师示例：分别把栈、堆、元空间“撑爆”，看报错是什么。
 * 用法（项目根目录，Java 17 或 21）：
 *   java -Xss256k reference/JvmMemoryDemo.java stack      -> 打印递归深度，抛 StackOverflowError
 *   java -Xss1m   reference/JvmMemoryDemo.java stack      -> 递归深度明显更大
 *   java -Xmx32m  reference/JvmMemoryDemo.java heap       -> OutOfMemoryError: Java heap space
 *   java -Xmx32m  reference/JvmMemoryDemo.java intern     -> 字符串常量池在堆里：同样是 Java heap space
 *   java -XX:MaxMetaspaceSize=32m reference/JvmMemoryDemo.java metaspace -> OutOfMemoryError: Metaspace
 * 每种模式都捕获错误、打印错误信息后正常退出；只在本机内存里分配，参数限制了上限。
 */
public class JvmMemoryDemo {

    static int depth = 0;

    static void recurse() {
        depth++;
        recurse();          // 每调用一次，当前线程的栈上多一个栈帧
    }

    public static void main(String[] args) {
        String mode = args.length > 0 ? args[0] : "stack";
        try {
            switch (mode) {
                case "stack" -> {
                    try {
                        recurse();
                    } catch (StackOverflowError e) {
                        System.out.println("StackOverflowError，递归深度约 " + depth);
                    }
                }
                case "heap" -> {
                    List<byte[]> keep = new ArrayList<>();
                    while (true) keep.add(new byte[1024 * 1024]);       // 每次 1MB，一直被 keep 引用着
                }
                case "intern" -> {
                    List<String> keep = new ArrayList<>();
                    for (long i = 0; ; i++) keep.add(("gov-" + i).intern());   // 放进字符串常量池
                }
                case "metaspace" -> {
                    List<Object> keep = new ArrayList<>();
                    for (int i = 0; ; i++) {
                        ClassLoader loader = new ClassLoader(JvmMemoryDemo.class.getClassLoader()) { };
                        // 每个新的类加载器都会生成一个新的代理类，类的元数据放在元空间
                        keep.add(Proxy.newProxyInstance(loader, new Class<?>[]{Runnable.class}, (p, m, a) -> null));
                        if (i % 5000 == 0) System.out.println("已生成代理类 " + i + " 个");
                    }
                }
                default -> System.out.println("未知模式：" + mode);
            }
        } catch (OutOfMemoryError e) {
            System.out.println("OutOfMemoryError: " + e.getMessage());
        }
    }
}
