浅谈JVM内存模型
JAVA虚拟机在执行JAVA程序的时候,会把它管理的内存分成若干不同的数据区域,每个区域都有各自的用途。目前大致把JVM内存模型划分为五个区域:程序计数器,虚拟机栈,本地方法栈,堆和方法区。


程序计数器
程序计数器(ProgramCounterRegister)是当前线程所执行的字节码的行号指示器。这句话理解起来有点拗口,打个比方,我看书看到一半的时候突然接到领导电话”XX啊,线上那个项目出BUG了,赶紧来公司加个班解决!“,这时书没看完啊怎么办?我会在当前页夹个书签,以便下次再看的时候接着上次看的地方往下读。而程序计数器就是这样一个作用。我们的CPU多线程处理能力有限,常规的CPU也就是4核8线程,表示同一时间段最多能同时处理8个线程。而我们的程序往往是几百上千个线程在跑。所以不得不采用线程之间来回切换的形式来执行程序。程序计数器就是线程的”书签“,用来记录当前线程执行到方法的哪一步,以便下次线程切回来的时候从上次执行的内存地址继续执行。程序计数器是线程私有的,各个线程之间的计数器互不影响,独立存储。程序计数器也是唯一在Java 虚拟机规范中没有规定任何OutOfMemoryError 的区域。
ps:JVM还有个东西叫方法计数器,是用来记录方法执行次数的,用于JIT。当方法执行次数达到阈值的时候,JVM会判定该方法为热点方法,从而将该方法编译为机器码,从而提高执行效率,两者概念别搞混淆了。
虚拟机栈
虚拟机栈(Java Virtual Machine Stacks)与程序计数器一样,也是线程私有的,它的生命周期与线程相同。我们JAVA程序中的所有线程都被它管理。线程是什么?网上这种概念一找一大堆,我的理解很简单,线程就是方法的执行者,java程序中所有方法只能被线程执行。一个用户请求过来就创建了一个线程,一直到请求回应这个线程生命也走到了尽头。该请求在我们的服务端执行了哪些操作都是在这个线程中实现的,线程每执行一个方法就会创建一个栈帧,栈帧用来存储当前方法的局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从被调用至返回的过程, 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
在Java 虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度(如循环嵌套/死循环),将抛出StackOverflowError 异常;如果虚拟机栈无法申请到足够的内存时会抛出OutOfMemoryError 异常。ps:普通线程大概消耗1M左右的内存,如果项目中线程数过多也会导致在该区域内存溢出,抛出OutOfMemoryError 异常。所以项目中能用线程池就用线程池限制和维护线程数量。
本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈非常类似,只不过虚拟机栈为虚拟机执行Java 方法服务,而本地方法栈则是为虚拟机使用到的native 方法服务。我们看JDK源码的时候经常看到有的方法前面有native修饰,这些都是本地方法,由非JAVA语言实现。
示例如下
package java.lang;
public class Object{
private static native void registerNatives();
...
...
public final native Class<?> getClass();
public native int hashCode();
protected native Object clone() throws CloneNotSupportedException;
public final native void notify();
...
}
与 虚拟机栈类似,本地方法栈也会抛出StackOverflowError和OutOfMemoryError 异常。以前项目中遇到过这种场景,遍历获取服务器某个目录下面所有文件夹和文件的时候抛出OutOfMemoryError 异常,提示是native方法报错。这时候别被native误导,native方法轻易不抛异常,就算抛异常也是我们打开方式有误,结合场景推测应该是有大文件在该目录下从而导致内存溢出,然后果然找到了大文件。
Java 堆
JAVA堆(Heap)是JVM内存管理中区域最大一块,也是GC垃圾回收器最活跃的区域,我们所有对象的生命周期都在堆里面。由于堆里面所有数据都是对虚拟机栈中所有线程共享,所以会造成并发编程的时候线程不安全的问题,这个我们先不讨论。现代GC主要采用的是分代回收的策略,将我们的堆主要划分为新生代(Eden区、From Survivor区和To Survivor区)和老年代。新生代就像炼狱,里面的对象朝生暮死,每分每秒都在煎熬,熬不下去就game over被扔到GC的销毁队列挨个销毁,熬下去了就跑到老年代去颐享天年。JVM默认配置一个对象如果经历了15次GC回收都还存活的话,就转移到老年代,特殊的大对象(如数组)除外。根据Java 虚拟机规范的规定,当JAVA堆无法满足内存分配需求时,将会抛出OutOfMemoryError 异常。ps:老年代不会轻易GC,但是老年代空间有限的情况下如果空间满了,则会促使GC来次大扫除--FULL GC,FULL GC是非常影响性能的,因为在执行FULL GC的时候,其他所有线程都不得不停下来等待,也就是所谓的STOP THE WORLD,一个好的JVM配置,基本不会出现 FULL GC的情况。
方法区
方法区(Method Area)存放虚拟机加载的类信息,静态变量,常量等数据。根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。同堆一样,方法区也是对所有线程共享的。 JDK1.8之前,大家习惯于把方法区称之为”永久代“,这是因为HotSpot 虚拟机的设计团队选择把GC 分代收集扩展到了方法区,但是为了跟堆区分出来又取了一个别名叫非堆。1.8以后用”元空间“取代了永久代的概念,元空间不再是jvm内存的一部分,而是直接在于本机内存中。而将常量池移到堆中。
希望这篇文章能给大家一些提示。
浅谈JVM内存模型的更多相关文章
- 老李谈JVM内存模型
老李谈JVM内存模型 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨 ...
- 浅谈Java内存模型
Java内存模型虽说是一个老生常谈的问题 ,也是大厂面试中绕不过的,甚至初级面试也会问到.但是真正要理解起来,还是相当困难,主要这个东西看不见,摸不着.网上已经有大量的博客,但是人家的终究是人家的,自 ...
- 浅谈JVM - 内存结构(二)- 虚拟机栈|凡酷
2.1 定义 Java Virtual Machine Stacks(Java虚拟机栈) Java 虚拟机栈描述的是 Java 方法执行的内存模型,用于存储栈帧,是线程私有的,生命周期随着线程启动而产 ...
- 浅谈JVM内存分配与垃圾回收
大家好,我是微尘,最近又去翻了周志明老师的<深入理解Java虚拟机>这本书.已经看了很多遍了,每次都感觉似乎看懂了,但没过多久就忘了.这次翻了第三章的垃圾收集器与内存分配策略,感觉有了新的 ...
- 浅谈JVM内存区域划分
好吧,虽说真的有看过<深入分析Java Web技术内幕>一书,但当时看的时候还是一知半解,稀里糊涂的看完了.本来是打算暑假拿起来再看一遍的,但是早两天一个阿里学长给我做了个小面试,让我颇受 ...
- 浅谈jvm中的垃圾回收策略
下面小编就为大家带来一篇浅谈jvm中的垃圾回收策略.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 java和C#中的内存的分配和释放都是由虚拟机自动管理的,此前我已 ...
- JVM内存模型与GC算法(简介)
JVM内存模型如上图,需要声明一点,这是<Java虚拟机规范(Java SE 7版)>规定的内容,实际区域由各JVM自己实现,所以可能略有不同.以下对各区域进行简短说明. 1.1程序计数器 ...
- JVM内存模型和结构详解(五大模型图解)
JVM内存模型和Java内存模型都是面试的热点问题,名字看感觉都差不多,实际上他们之间差别还是挺大的. 通俗点说,JVM内存结构是与JVM的内部存储结构相关,而Java内存模型是与多线程编程相关@mi ...
- JVM内存模型、指令重排、内存屏障概念解析
在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...
随机推荐
- 02JavaScript基础语法及数据类型
JavaScript基础语法及数据类型 2.1数据类型 2.1.1字符串(String) 用单引号或双引号括起来的零个或多个单一的字符所组成. 2.1.2数值(Number) 包含整数或浮点数. 2. ...
- java面试题链接
http://blog.csdn.net/jackfrued/article/details/17339393
- 去重 取最大的一条sql
select T.BILL_CODE,t.SCAN_TYPE,t.PIECE,SCAN_SITE,SCAN_MAN, row_number() over(partition by t.bill_cod ...
- UVA-227 Puzzle(模拟)
题目: 题目浏览传送门 题意: 给出一个5*5的方格,里边有一个格子是空的,现在给出一串指令,A->空格向上移动,B->空格向下移动,R->空格向右移动,L->空格向左移动. ...
- 深度完整的了解MySQL锁
今天就讲讲MySQL的锁 主讲:Mysql的悲观锁 和 乐观锁官方:If you query data and then insert or update related data within th ...
- PHP+Python,轻量维护超轻松
window下安装Python 直接去Python官网下载msi安装程序安装即可(选择自定义安装才能修改程序安装路径哦) widows下Python的数据库访问模块PyMysql的安装 1.open ...
- Python-函数和代码复用
函数的定义与使用 >函数的理解与定义 函数是一段代码的表示 -函数是一段具有特定功能的.可重用的语句组 -函数是一种功能的抽象,一般函数表达特定功能 -两个作用:降低编程难度 和 代码复用 de ...
- Java基础学习总结(86)——Java异常处理机制Exception抛出异常时throw和throws用法详解
什么时运行时异常?什么是非运行时异常? 通俗的讲: 运行时异常:就是编译通过,运行时就崩了,比如数组越界. 非运行时异常:就是编译不通过,这时就得必须去处理了.不然就没法运行了. 全面的讲: Thro ...
- BNUOJ 14381 Wavio Sequence
Wavio Sequence Time Limit: 3000ms Memory Limit: 131072KB This problem will be judged on UVA. Origina ...
- Leetcode 115.不同的子序列
不同的子序列 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数. 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串.(例 ...