运行时数据区 - JVM内存模型

PC寄存器(program counter register)

Java虚拟机可以支持多线程同时执行。每个Java虚拟机线程拥有自己的PC寄存器。在任何时候,每个Java虚拟机线程都在执行单个方法代码,即该线程的当前方法。如果当前方法不是native,PC寄存器包含Java虚拟机当前正在执行的指令地址;如果当前方法是native,PC寄存器的值为undefined。PC寄存器在特定的平台上有足够的宽度存储returnAddress或native指针。

JVM栈(Java Virtual Machine Stacks)

每个Java虚拟机线程都有一个与线程同时创建的私有Java虚拟机栈,Java虚拟机栈存储帧(Frames),java虚拟机栈类似于传统语言的栈,例如C:它保存局部变量和部分结果,是方法调用和返回的一部分。Java虚拟机栈除了push和pop帧之外,永远不会直接操作Java虚拟机堆栈,堆可以分配帧。Java虚拟机栈不需要连续存储。

Java虚拟机规范允许Java虚拟机栈具有固定大小或根据计算的需要动态扩展和收缩。如果Java虚拟机栈具有固定大小,则可以在创建该栈时独立选择每个Java虚拟机栈的大小。
Java虚拟机未实现虚拟机栈动态扩展,动态扩展通常有两种方法:
1. Segmented stack:一个双向链表把多个栈连接起来,一开始只分配一个栈,这个栈的空间不够时,就再分配一个,用链表一个一个连起来。
2. Stack coping:在栈不够的时候,分配一个更大的栈,然后把原来的栈复制过去。

-Xss
-XX:ThreadStackSize

设置线程堆栈大小(以字节为单位)。附加字母k或K表示KB,m或M表示MB,g或G表示GB。默认值取决于平台:
Linux / ARM(32位):320 KB
Linux / i386(32位):320 KB
Linux / x64(64位):1024 KB
OS X(64位):1024 KB
Oracle Solaris / i386(32位):320 KB
Oracle Solaris / x64(64位):1024 KB

StackOverFlowError:

public StackOverFlowTest {
    private static int stackDepth = 0;
    public static void stackLeak() {
        stackDepth++;
        stackLeak();
    }

    public static void main(String args[]) {
        stackLeak();
    }
}

OutOfMemoryError: Java heap space

public OutOfMemoryTest {
    private static List<Long[]> list = new ArrayList<>();
    public static void heapLeak() {
        while(true) {
            list.add(new Long[1024]);
        }
    }

    public static void main(String args[]) {
        heapLeak();
    }
}

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

帧(Frames)

堆(Heap)

Java虚拟机具有在所有Java虚拟机线程之间共享的堆。堆是分配所有类实例和数组的内存的运行时数据区。
堆是在虚拟机启动时创建的。对象的存储由自动存储管理系统(GC)回收;对象永远不会被显式释放。Java虚拟机假设没有特定类型的自动存储管理系统,可以根据实现者的系统要求选择存储管理技术。堆可以具有固定大小,或者可以根据计算的需要进行扩展,并且如果不需要更大的堆,则可以收缩。堆的内存不需要连续。

-Xms
-Xmx

-XX:InitialHeapSize
-XX:MaxHeapSize

OutOfMemoryError

方法区(Method Area) – 非堆内存

Java虚拟机具有在所有Java虚拟机线程之间共享的方法区域。方法区域类似于传统语言的编译代码的存储区域或类似于操作系统进程中的“文本”段。它存储每个类的结构,例如:运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法。
方法区域是在虚拟机启动时创建的。虽然方法区域在逻辑上是堆的一部分,但是简单的实现可能选择不垃圾收集或压缩它。本规范未规定方法区域的位置或用于管理编译代码的策略。方法区域可以是固定大小的,或者可以根据计算的需要进行扩展,并且如果不需要更大的方法区域,则可以缩小方法区域。方法区域的内存不需要连续。

-XX:PermSize
-XX:MaxPermSize

// For JDK8:
-XX:MetaspaceSize
-XX:MaxMetaspaceSize

OutOfMemoryError: Metaspace

public OutOfMemoryTest {
    public static void methodAreaOOM() {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object
                        obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    return proxy.invokeSuper(obj, args);
                }
            });
            enhancer.create();
        }
    }

    public static void main(String args[]) {
        methodAreaOOM();
    }

    static class OOMObject {}
}

运行时常量池(Run-Time Constant Pool)

运行时常量池是类或接口运行时class文件中constant_pool表的表现形式。它包含几种常量,从编译时已知的数字文字到必须在运行时解析的方法和字段引用。运行时常量池提供类似于传统编程语言的符号表的功能,尽管它包含比典型符号表更宽范围的数据。
每个运行时常量池都是从Java虚拟机的方法区中分配的。当Java虚拟机创建类或接口时,将构造类或接口的运行时常量池。

OutOfMemoryError: Java heap space

public OutOfMemoryTest {
    // For JDK8
    public static void contantsPoolOOM() {
        List<String> refList = new ArrayList<>();
        int i = 129;
        while (true) {
            refList.add(String.valueOf(i++).intern());
        }
    }

    public static void main(String args[]) {
        contantsPoolOOM();
    }
}

本地方法栈(Native Method Stacks)

Java虚拟机的实现可以使用常规堆栈(俗称“C堆栈”)来支持native方法(用Java编程语言以外的语言编写的方法)。本地方法栈也可以通过以诸如C语言的Java虚拟机的指令集的解释器的实现来使用。无法加载native方法并且本身不依赖于传统栈的Java虚拟机实现不需要提供本地方法栈。如果提供,则通常在创建每个线程时为每个线程分配本地方法栈。

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。

-Xss
-XX:ThreadStackSize

OutOfMemoryError

https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值