一:虚拟机内存图解
JAVA程序运行与虚拟机之上,运行时需要内存空间。虚拟机执行JAVA程序的过程中会把它管理的内存划分为不同的数据区域方便管理。
虚拟机管理内存数据区域划分如下图:
数据区域分类:
方法区 (Method Area)
虚拟机栈 (VM Stack)
本地方法栈 (Native Method Stack)
堆 (Heap)
程序计数器 (Program Counter Register)
直接内存 (Direct Memory)
说明:
1. 程序计数器
行号指示器,字节码指令的分支、循环、跳转、异常处理、线程恢复(CPU切换),每条线程都需要一个独立的计数器,线程私有内存互不影响,该区域不会发生内存溢出异常。
2. 虚拟机栈
是线程私有的,声明周期与线程相同,虚拟机栈是Java方法执行的内存模型,每个方法被执行时都会创建一个栈帧,即方法运行期间的基础数据结构,栈帧用于存储:局部变量表、操作数栈、动态链接、方法出口等,每个方法执行中都对应虚拟机栈帧从入栈到处栈的过程。
是一种数据结构,是虚拟机中的局部变量表,对应物理层之上的程序数据模型。
局部变量表,是一种程序运行数据模型,存放了编译期可知的各种数据类型例如:
Boolean、byte、char、short、int、float、long、double、对象引用类型(对象内存地址变量,指针或句柄),程序运行时,根据局部变量表分配栈帧空间大小,在运行中,大小是不变的异常类型:stackOverFlowError 线程请求栈深度大于虚拟机允许深度 OutOfMemory 内存空间耗尽无法进行扩展。
3. 本地方法栈
与虚拟机栈类似,虚拟机栈为Java程序服务,本地方法栈支持虚拟机的运行服务,具体实现由虚拟机厂商决定,也会抛出 stackOverFlowError、OutOfMemory异常。
4. 堆
是虚拟机管理内存中最大的一部分,被所有线程共享,用于存放对象实例(对象、数组),物理上不连续的内存空间,由于GC收集器,分代收集,所以划分为:新生代 Eden、From SurVivor空间、To SurVivor空间,allot buffer(分配空间),可能会划分出多个线程私有的缓冲区,老年代。
5. 方法区
与堆一样属于线程共享的内存区域,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码(动态加载OSGI)等数据。理论上属于java虚拟机的一部分,为了区分开来叫做 Non-Heap非堆。
这个区域可以选择不进行垃圾回收,该区域回收目的主要是常量池的回收,及类型的卸载class,内存区不足时会抛出OutOfMemory异常
运行时常量池:
方法区的一部分,Class的版本、字段、接口、方法等,及编译期生成的各种字面量、符号引用,编译类加载后存放在该区域。会抛出OutOfMemory异常。
6. 直接内存
直接内存不属于虚拟内存区域,是一种基于通道与缓冲区的IO方式,可以使用本地函数直接分配堆外内存,在堆中存储引用的外部内存地址,通过引用完成对直接引用内存的操作,1.4之后提供的NIO显著提高效率,避免了堆内存与Native内存的来回复制操作,不受虚拟机内存控制,会抛出OUtOfMemory异常。
二:对象访问内部实现过程
对象访问 涉及到对象的地址变更状态变更,内存地址移动,变量、接口、实现类、方法、父类型等。
一、 句柄方式 (访问)
二、指针方式 (访问)
优缺点:
句柄访问方式:reference中存储的是稳定的地址,对象变更时只会改变句柄实例数据指针,引用本身不需要修改
指针访问方式:优点速度快,节省了指针定位时间开销
三:内存区域控制参数及对应溢出异常
开发过程中,或程序运行过程中每次遇到OutOfMemory异常或GC异常或StackOverflowError异常我们都是一堆参数乱配,都把值调大,只是大体知道是跟jvm内存分配有关,具体应该怎么调,对应的异常应该调整那些参数,或者换句话说,jvm内存分配区域中都分别对应那些参数大多数情况下都是不知道的,只是把相关的参数跳上去,预期结果都是应该起作用,到底能不能起作用,自己心里也没底。下面就来说一下jvm堆、栈、方法区等内存区域对应的参数,及每个区域可能抛出的异常类型,发生异常的场景分析。
一、参数类型
1.堆空间参数
2.栈空间参数
3.方法区空间参数
4.本机直接内存参数
二、异常类型
1.OutOfMemory异常
2.StackOverflowError异常
三、辅助参数说明
1.-XX:+HeapDumpOnOutOfMemoryError 打印堆内存异常时打印出快照信息
2.-XX:+HeapDumpPath 快照输出路径
3.-Xmn指定eden区的大小 -XX:SurvirorRation来调整幸存区的大小
4.-XX:PretenureSizeThreshold设置进入老年代的阀值
四、参数说明、对应场景的异常
1.堆内存参数
-Xms:堆最小值(新生代和老年代之和)
-Xmx:堆最大值(新生代和老年代之和)
当最小值=最大值时,这时堆内存是不可扩展的。
例:-Xms80M -Xmx80M
通常将-Xmx和-Xms设置为一样的大小来减少gc的次数,堆内存不足时抛出OutOfMemoryError异常。
2.栈内存参数
-Xss
例:-Xss128k