浅析JVM内存结构和6大区域
内存作为系统中重要的资源,对于系统稳定运行和高效运行起到了关键的作用,Java和C之类的语言不同,不需要开发人员来分配内存和回收内存,而是由JVM来管理对象内存的分配以及对象内存的回收(又称为垃圾回收、GC),这对于开发人员来说确实大大降低了编写程序的难度,但带来的一个副作用就是,当系统运行过程中出现JVM抛出的内存异常(例如OutOfMemoryError)的时候,很难知道原因是什么,另外一方面,要编写高性能的程序,通常需要借助内存来提升性能,因此如何才能合理的使用内存以及让JVM合理的进行内存的回收是必须掌握的,本文将主
其实对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘。那JVM的内存结构到底是如何呢?JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器、堆栈等区域。
JVM在运行时将数据划分为了6个区域来存储,而不仅仅是大家熟知的Heap区域,这6个区域图示如下:
JVM内存的分配结构示意图
下面将逐一介绍下各个区域所做的工作及其充当的功能。
PC Register(PC寄存器)
PC寄存器是一块很小的内存区域,主要作用是记录当前线程所执行的字节码的行号。字节码解释器工作时就是通过改变当前线程的程序计数器选取下一条字节码指令来工作的。任何分支,循环,方法调用,判断,异常处理,线程等待以及恢复线程,递归等等都是通过这个计数器来完成的。
由于Java多线程是通过交替线程轮流切换并分配处理器时间的方式来实现的,在任何一个确定的时间里,在处理器的一个内核只会执行一条线程中的指令。因此为了线程等待结束需要恢复到正确的位置执行,每条线程都会有一个独立的程序计数器来记录当前指令的行号。计数器之间相互独立互不影响,我们称这块内存为“线程私有”的内存。
如果所调用的方法为native的,则PC寄存器中不存储任何信息。
l JVM栈
JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址,因此Java中基本类型的变量是值传递,而非基本类型的变量是引用传递,Sun JDK的实现中JVM栈的空间是在物理内存上分配的,而不是从堆上分配。
由于JVM栈是线程私有的,因此其在内存分配上非常高效,并且当线程运行完毕后,这些内存也就被自动回收。
当JVM栈的空间不足时,会抛出StackOverflowError的错误,在Sun JDK中可以通过-Xss来指定栈的大小,例如如下代码:
- new Thread(new Runnable(){
- public void run() {
- loop(0);
- }
- private void loop (int i){
- if(i!=1000){
- i++;
- loop (i);
- }
- else{
- return;
- }
- }
- }).start();
当JVM参数设置为-Xss1K,运行后会报出类似下面的错误:
Exception in thread "Thread-0"java.lang.StackOverflowError
l 堆(Heap)
Heap是大家最为熟悉的区域,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收,Heap在32位的操作系统上最大为2G,在64位的操作系统上则没有限制,其大小通过-Xms和-Xmx来控制,-Xms为JVM启动时申请的最小Heap内存,默认为物理内存的1/64但小于1G,-Xmx为JVM可申请的最大Heap内存,默认为物理内存的1/4,默认当空余堆内存小于40%时,JVM会增大Heap的大小到-Xmx指定的大小,可通过-XX:MinHeapFreeRatio=来指定这个比例,当空余堆内存大于70%时,JVM会将Heap的大小往-Xms指定的大小调整,可通过-XX:MaxHeapFreeRatio=来指定这个比例,但对于运行系统而言,为了避免频繁的Heap
Size的大小,通常都会将-Xms和-Xmx的值设成一样,因此这两个用于调整比例的参数通常是没用的。其实jvm中对于堆内存的分配、使用、管理、收集等有更为精巧的设计,具体可以在JVM堆内存分析中进行详细介绍。
当堆中需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。
l 方法区域(MethodArea)
方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,可见方法区域的重要性。同样,方法区域也是全局共享的,它在虚拟机启动时在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。
在Sun JDK中这块区域对应的为PermanetGeneration,又称为持久代,默认为64M,可通过-XX:PermSize以及-XX:MaxPermSize来指定其大小。
l 运行时常量池(RuntimeConstant Pool)
类似C中的符号表,存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。类或接口的常量池在该类的class文件被java虚拟机成功装载时分配。
l 本地方法堆栈(NativeMethod Stacks)
JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。
例如有这么一段代码:
- public class A {
- public static void main(String[]args){
- String a="a";
- String b="b";
- String ab="ab";
- System.out.println((a+b)==ab); // false
- System.out.println(("a"+"b")==ab); // true
- final String afinal="a";
- String result=afinal+"b";
- System.out.println(result==ab); // true
- String plus=a+"b";
- System.out.println(plus==ab); // false
- System.out.println(plus.intern()==ab); // true
- }
- }
分析下上面代码执行的结果,可通过javap –verbose A来辅助理解分析。
l (a+b)==ab
a+b是两个变量相加,需要到运行时才能确定其值,到运行时后JVM会为两者相加后产生一个新的对象,因此a+b==ab的结果为false。
l (“a”+”b”)==ab
“a”+”b”是常量,在编译时JVM已经将其变为”ab”字符串了,而ab=”ab”也是常量,这两者在常量池即为同一地址,因此(“a”+”b”)==ab为true。
l result==ab
result=afinal+”b”,afinal是个final的变量, result在编译时也已经被转变为了”ab”,和”ab”在常量池中同样为同一地址,因此result==ab为true。
l plus=ab
plus和a+b的情况是相同的,因此plus==ab为false。
l plus.intern()==ab
这里的不同点在于调用了plus.intern()方法,这个方法的作用是获取plus指向的常量池地址,因此plus.intern()==ab为true。
在掌握了JVM对象内存分配的机制后,接下来看看JVM是如何做到自动的对象内存回收的,这里指的的是Heap以及Method Area的回收,其他几个区域的回收都由JVM简单的按生命周期来进行管理
原文链接:http://blog.csdn.net/zhaozheng7758/article/details/8623562
浅析JVM内存结构和6大区域的更多相关文章
- 浅析JVM内存结构和6大区域(转)举例非常好
内存作为系统中重要的资源,对于系统稳定运行和高效运行起到了关键的作用,Java和C之类的语言不同,不需要开发人员来分配内存和回收内存,而是由JVM来管理对象内存的分配以及对象内存的回收(又称为垃圾回收 ...
- 浅析JVM内存结构和6大区域(转)
其实对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再f ...
- JVM内存结构和6大区域
摘自 http://www.iteye.com/news/30350 对于我们一般理解的计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据 ...
- 浅析JVM内存区域及垃圾回收
一.JVM简介 JVM,全称Java Virtual Machine,即Java虚拟机.以Java作为编程语言所编写的应用程序都是运行在JVM上的.JVM是一种用于计算设备的规范,它是一个虚构出来的计 ...
- jvm系列(二):JVM内存结构
JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...
- JVM内存结构
前言 在Java语言开发过程中,out of memory错误是很常见的一种错误.对于JVM的内存结构有更深入的了解,更更好的帮我们排查此类问题,有效的避免此类问题发生.在JAVA 8中内存结构有进行 ...
- 管中窥豹——从对象的生命周期梳理JVM内存结构、GC调优、类加载、AOP编程及性能监控
如题,本文的宗旨既是透过对象的生命周期,来梳理JVM内存结构及GC相关知识,并辅以AOP及双亲委派机制原理,学习不仅仅是海绵式的吸收学习,还需要自己去分析why,加深对技术的理解和认知,祝大家早日走上 ...
- jvm系列二、JVM内存结构
原文链接:http://www.cnblogs.com/ityouknow/p/5610232.html 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemory ...
- JVM内存结构之堆、栈、方法区以及直接内存、堆和栈区别
JVM内存结构之堆.栈.方法区以及直接内存.堆和栈区别 一. 理解JVM中堆与栈以及方法区 堆(heap):FIFO(队列优先,先进先出):二级缓存:*JVM中只有一个堆区被所有线程所共享:对象和数 ...
- JVM活学活用——Jvm内存结构
Java内存结构: JVM内存结构主要是有三大块:堆内存.方法区和栈.堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分为三部分,Eden空间.From Survivor空间.To S ...
随机推荐
- vue打包后dist的使用
发现问题 vue项目完成打包出dist后准备打开index.html,发现居然页面是一片空白,f12一片报红. 分析问题 经过多次网上查询后发现这是由于vue打包时,脚手架会帮你配置好大量参数,但其中 ...
- linux、unix软链接注意事项
前言 在使用linux过程中,经常使用到软链接(类似windows快捷方式): 创建软链接之后,删除时不注意就会出现到问题 先说结论 删除软链接,确实是使用rm进行删除:但是有个小细节必须要特别注意! ...
- Vue3——Vite + element-plus +Vue3 项目搭建、"@"别名设置
1. 环境准备 node 官网 npm 切换国内 npm 源镜像 npm config set registry https://registry.npmmirror.com 查看当前的镜像源 npm ...
- python3 numpy的一些小知识点
简介 一个用python实现的科学计算,包括: 1.一个强大的N维数组对象Array: 2.比较成熟的(广播)函数库: 3.用于整合C/C++和Fortran代码的工具包: 4.实用的线性代数.傅里叶 ...
- 均值回归策略在A股ETF市场获利的可能性
如何在股票市场获利 曾经有人告诉我一个在股票市场赚钱的秘诀,只要掌握这个秘诀,赚钱就像捡钱一样容易.他说:这个秘诀其实很简单,就是在股票价格低的时候买入,在价格高的时候卖出. 啧啧,不愧是秘诀,明明是 ...
- foobar2000 v2.1.3 汉化版(更新日期:2024.04.02)
foobar2000 v2.1.3 汉化版 -----------------------[软件截图]---------------------- -----------------------[软件 ...
- Blazor 子组件与父组件通过 ChildEvents 传递数据的方法
想要实现 Blazor 子组件向父组件传递数据, 参考 痴者工良的博文所描述的方式, .Net 5.0 下编译未能通过, 于是先修改一下, 简化为光触发事件通知而不传值 子组件 Child.razor ...
- USB协议详解第11讲(USB描述符-总结)
描述符回顾总结 1.其实所有的描述符都是USB设备用来描述自己属性及用途的,所以必须在设备端实现对应的描述符,主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备.设备需要的总 ...
- DBA面试资源合集(含Oracle、MySQL、Redis等)-墨天轮
如今正值金九银十招聘季,众多企业开放大批岗位等待新力量的注入,各位DBA们,你们是否正在激情备战中? 作为企业数据化进程中十分重要的一环,DBA的职责越来越重要,作为高薪资岗位之一,应聘DBA的竞争也 ...
- replace 和 replaceAll
replace 匹配中的第一次:replaceAll 替换所有匹配的内容: let str = 'aj123dshf12aaaaaaaaaaa3uausdjd123suas123fus12' let ...