异常将上下文初始化事件发送到类的侦听器实例_深入浅出JVM性能调优——JVM内存

一、JVM内存模型
运行一个 Java 应用程序,必须要先安装 JDK 或者 JRE 包 。因为 Java 应用在编译后会变成字节码,通过字节码运行在 JVM 中,而 JVM 是 JRE 的核心组成部分 。JVM 不仅承担了 Java 字节码的分析和执行,同时也内置了自动内存分配管理机制 。这个机制可以大大降低手动分配回收机制可能带来的内存泄露和内存溢出风险,使 Java 开发人员不需要关注每个对象的内存分配以及回收,从而更专注于业务本身 。
在 Java 中,JVM 内存模型主要分为堆、方法区、程序计数器、虚拟机栈和本地方法栈 。其中,堆和方法区被所有线程共享,虚拟机栈、本地方法栈、程序计数器是线程私有的 。
1、堆
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中 。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和区,最后由 From和 To组成 。
但需要注意的是,这些区域的划分因不同的垃圾收集器而不同 。大部分垃圾收集器都是基于分代收集理论设计的,就会采用这种分代模型 。而一些新的垃圾收集器不采用分代设计,比如 G1 收集器就是把堆内存拆分为多个大小相等的。
2、方法区
在 jdk8 之前, 虚拟机的方法区又被称为永久代,由于永久代的设计容易导致内存溢出等问题,jdk8 之后就没有永久代了,取而代之的是元空间() 。元空间并没有处于堆内存上,而是直接占用的本地内存,因此元空间的最大大小受本地内存限制 。
方法区与堆空间类似,是所有线程共享的 。方法区主要是用来存放已被虚拟机加载的类型信息、常量、静态变量等数据 。方法区是一个逻辑分区,包含元空间、运行时常量池、字符串常量池,元空间物理上使用的本地内存,运行时常量池和字符串常量池是在堆中开辟的一块特殊内存区域 。这样做的好处之一是可以避免运行时动态生成的常量的复制迁移,可以直接使用堆中的引用 。要注意的是,字符串常量池在 jvm 中只有一个,而运行时常量池是和类型数据绑定的,每个 Class 一个 。
1)类型信息(类或接口):
2)运行时常量池:
3)字段信息:
4)方法信息:
5)指向类加载器的引用:
6)指向 Class 类的引用:
3、虚拟机栈
每当启动一个新的线程,虚拟机都会在虚拟机栈里为它分配一个线程栈,线程栈与线程同生共死 。线程栈以 栈帧 为单位保存线程的运行状态,虚拟机只会对线程栈执行两种操作:以栈帧为单位的压栈或出栈 。每个方法在执行的同时都会创建一个栈帧,每个方法从调用开始到结束,就对应着一个栈帧在线程栈中压栈和出栈的过程 。方法可以通过两种方式结束,一种通过正常返回,一种通过抛出异常而终止 。方法返回后,虚拟机都会弹出当前栈帧然后释放掉 。
当虚拟机调用一个Java方法时.它从对应类的类型信息中得到此方法的局部变量区和操作数栈的大小,并据此分配栈帧内存,然后压入Java栈中 。
栈帧由三部分组成:局部变量区、操作数栈、帧数据区 。
1)局部变量区:
2)操作数栈:
3)帧数据区:主要保存常量池入口、异常表、正常方法返回的信息
4、本地方法栈
本地方法栈与虚拟机栈所发挥的作用是相似的,当线程调用Java方法时,会创建一个栈帧并压入虚拟机栈;而调用本地方法时,虚拟机会保持栈不变,不会压入新的栈帧,虚拟机只是简单的动态链接并直接调用指定的本地方法,使用的是某种本地方法栈 。比如某个虚拟机实现的本地方法接口是使用C连接模型,那么它的本地方法栈就是C栈 。