(二)Java虚拟机内存区域与内存溢出异常
发布日期:2021-05-28 18:23:16 浏览次数:25 分类:精选文章

本文共 2120 字,大约阅读时间需要 7 分钟。

在类加载完之后,JVM会做以下事情吗?让我详细解释一下整个过程。
  • 当JVM成功地将一个类加载到方法区后,接下来它会为新创建的对象分配内存。这个内存肯定是位于堆区中的,因为新对象的信息至少需要存储在某个地方。

  • 然后,如果你试图创建一个新对象,例如’ve new Student();’,JVM会立即加载相关的 Student 类。如果之前类加载检查已经完成,那么它会毫不犹豫地为这个对象分配堆区的内存。这个内存管理过程是通过调用系统所支持的内存管理机制完成的,这也意味着如果堆区内存不够用时,JVM会抛出OutOfMemoryError(OOM)异常。

  • 让我们深入理解一下JVM的内存结构和对象管理机制:

    JVM的内存结构包括以下几个部分:

    1.1 程序计数器

    程序计数器主要用于记录当前线程所执行的字节码行号。它是线程私有的,当一个CPU在不同的线程间切换时,程序计数器可以完全独立地工作。你应该知道,程序计数器是依赖于它完成线程切换和执行控制流的每一个跳转操作,所以它在多线程执行中非常关键。

    1.2 虚拟机栈

    虚拟机栈是为每个线程分配的,每个方法执行时都会创建一个栈帧。栈帧包含调用方法时所需的信息,比如本地方法表、本地变量表等。特别是当递归或深度迭代发生时,虚拟机栈可能会因为栈帧数量过多而导致 StackOverflowError。

    1.3 本地方法栈

    本地方法栈和虚拟机栈的功能类似,但它是为本地(Native)方法服务的。此外,本地方法栈也会有栈溢出(StackOverflowError)和内存溢出(OutOfMemoryError)的异常。

    1.4 Java 堆

    Java 堆是所有线程共享的一块内存区域,主要存放对象实例和数组。垃圾回收器的工作重点就在这里。如果你不断地创建对象而不进行有效的内存管理,堆区的内存很快就会被用完。这时候,JVM就会抛出OutOfMemoryError。

    1.5 方法区

    方法区是一个全共享的内存区域,主要用来存储类的元数据、静态变量以及编译后的常量值。与堆不同,方法区有时也被称为“非堆”(Non-Heap),因为它通常不会参与垃圾回收过程。不过,也有一些实现可能会将方法区纳入堆中的“永久代”。

    1.6 运行时常量池

    运行时常量池是方法区的一部分,主要存放编译期生成的字面量和符号引用。如果你使用了字符串的 intern() 方法,就会将常量存储在这里。如果常量池内存不够用时,也会抛出OutOfMemoryError。

    此外,直接内存也属于内存的一部分,它允许某些本地方法直接分配内存。例如,在Java I/O操作中使用的DirectByteBuffer就是利用了直接内存。

    内存溢出的处理

    内存溢出(OutOfMemoryError)是Java程序在内存管理中可能遇到的一个常见问题。具体表现形式可以有以下几种:

    堆溢出

    堆溢出是最常见的内存溢出类型。例如,当你试图在堆中创建无限数量的对象时,堆内存会被逐渐消耗完毕,导致JVM无法分配内存,从而抛出堆溢出异常。你可以通过一个简单的循环测试这一点:

    List
    list = new ArrayList<>();
    while (true) {
    list.add(new String(new char(100000).intern()));
    }

    栈溢出

    栈溢出通常发生在递归或深层次的迭代中。例如,以下代码会导致栈溢出:

    private Integer stackLength = 1;
    public void stackLoop() {
    stackLength++;
    stackLoop();
    }
    public static void main(String[] args) {
    OutOfMemoryError a = new OutOfMemoryError();
    try {
    a.stackLoop();
    } catch (Throwable e) {
    System.out.println("stack length: " + a.stackLength);
    throw e;
    }
    }

    方法区溢出

    方法区溢出相对少见,但它可以通过动态生成大量类来模拟。例如,使用CGLib动态生成类,当方法区内存不够时,就会抛出溢出异常:

    public class JavaMethodAreaOOM {
    public static void main(String[] args) {
    while (true) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(OOMObject.class);
    enhancer.create();
    }
    }
    static class OOMObject {
    }
    }
    上一篇:Java使用poi做加自定义注解实现对象与Excel相互转换
    下一篇:(一)Java的类加载机制

    发表评论

    最新留言

    网站不错 人气很旺了 加油
    [***.192.178.218]2025年05月03日 18时00分38秒

    关于作者

        喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
    -- 愿君每日到此一游!

    推荐文章

    Kubernetes学习总结(7)——学习 Kubernetes 的 Pod 2025-04-03
    Kubernetes学习总结(8)—— Kubernetes Pod 资源管理 和 Pod 服务质量 2025-04-03
    Kubernetes学习总结(9)—— 基础架构的未来是 K8s,那么 K8s 的未来在何方? 2025-04-03
    kubernetes实战(十三):k8s使用helm持久化部署harbor集成openLDAP登录 2025-04-03
    Kubernetes实战(一)-Kubernetes集群搭建 2025-04-03
    Kubernetes实战(七)-优先级调度(Pod Priority Preemption) 2025-04-03
    Kubernetes实战(三十一)-Calico网络部署(推荐) 2025-04-03
    Kubernetes实战(三十三)-外部Etcd集群部署与调优(更安全的数据存储策略) 2025-04-03
    Kubernetes实战(三十二)-Kubeadm 安装 Kubernetes v1.24.0 2025-04-03
    Kubernetes实战(三十)-HTTP 7层路由机制(Ingress) 2025-04-03
    Kubernetes实战(三)-定向调度(NodeSelector) 2025-04-03
    Kubernetes实战(九)-初始化容器(Init Container) 2025-04-03
    Kubernetes实战(二十七)-集群访问外部服务(Endpoints) 2025-04-03
    Kubernetes实战(二十三)-kubernetes二进制文件方式部署集群(安全)(上) 2025-04-03
    Kubernetes实战(二十九)-集群资源管理(CPU & Memory) 2025-04-03
    Kubernetes实战(二十二)-Etcd 集群部署(安全) 2025-04-03
    Kubernetes实战(二十五)-Flannel 网络部署(不推荐,不支持 Etcd3) 2025-04-03
    Kubernetes实战(二十八)-环境共享与隔离(Namespace) 2025-04-03
    Kubernetes实战(二十六)-高可用负载均衡与外部服务(Service) 2025-04-03
    Kubernetes实战(二十四)-kubernetes二进制文件方式部署集群(安全)(下) 2025-04-03