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内存模型的更多相关文章

  1. 老李谈JVM内存模型

    老李谈JVM内存模型   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨 ...

  2. 浅谈Java内存模型

    Java内存模型虽说是一个老生常谈的问题 ,也是大厂面试中绕不过的,甚至初级面试也会问到.但是真正要理解起来,还是相当困难,主要这个东西看不见,摸不着.网上已经有大量的博客,但是人家的终究是人家的,自 ...

  3. 浅谈JVM - 内存结构(二)- 虚拟机栈|凡酷

    2.1 定义 Java Virtual Machine Stacks(Java虚拟机栈) Java 虚拟机栈描述的是 Java 方法执行的内存模型,用于存储栈帧,是线程私有的,生命周期随着线程启动而产 ...

  4. 浅谈JVM内存分配与垃圾回收

    大家好,我是微尘,最近又去翻了周志明老师的<深入理解Java虚拟机>这本书.已经看了很多遍了,每次都感觉似乎看懂了,但没过多久就忘了.这次翻了第三章的垃圾收集器与内存分配策略,感觉有了新的 ...

  5. 浅谈JVM内存区域划分

    好吧,虽说真的有看过<深入分析Java Web技术内幕>一书,但当时看的时候还是一知半解,稀里糊涂的看完了.本来是打算暑假拿起来再看一遍的,但是早两天一个阿里学长给我做了个小面试,让我颇受 ...

  6. 浅谈jvm中的垃圾回收策略

    下面小编就为大家带来一篇浅谈jvm中的垃圾回收策略.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧   java和C#中的内存的分配和释放都是由虚拟机自动管理的,此前我已 ...

  7. JVM内存模型与GC算法(简介)

    JVM内存模型如上图,需要声明一点,这是<Java虚拟机规范(Java SE 7版)>规定的内容,实际区域由各JVM自己实现,所以可能略有不同.以下对各区域进行简短说明. 1.1程序计数器 ...

  8. JVM内存模型和结构详解(五大模型图解)

    JVM内存模型和Java内存模型都是面试的热点问题,名字看感觉都差不多,实际上他们之间差别还是挺大的. 通俗点说,JVM内存结构是与JVM的内部存储结构相关,而Java内存模型是与多线程编程相关@mi ...

  9. JVM内存模型、指令重排、内存屏障概念解析

    在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...

随机推荐

  1. CentOS \Linux文件权限详解

    文件和目录权限概述 在linux中的每一个文件或目录都包含有访问权限,这些访问权限决定了谁能访问和如何访问这些文件和目录. 通过设定权限可以从以下三种访问方式限制访问权限:只允许用户自己访问:允许一个 ...

  2. pxc增量备份

    ###增备数据库,如果后续还需要再次增备,则可以再次指定--extra-lsndir,如果与上次备份指定相同的位置,该文件被覆盖# innobackupex --compress --incremen ...

  3. 【3】数据筛选3 - BeautifulSoup4

    #目录     1. 开发前准备     2. 不同解析器对比     3. BeautifulSoup4 初始化和节点对象的认识     4. BS4 案例操作:初始化对象文档     5. 节点查 ...

  4. hdu 1754 I Hate It(线段树水题)

    >>点击进入原题测试<< 思路:线段树水题,可以手敲 #include<string> #include<iostream> #include<a ...

  5. Java Arrays.sort相关用法与重载

    Java Arrays.sort() Java中的数组排序函数, 头文件 import java.util.Arrays; 相关API Arrays.sort(arys[]) Arrays.sort( ...

  6. [bzoj1044][HAOI2008][木棍分割] (二分+贪心+dp+队列优化)

    Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...

  7. 调试pcb板子的步骤

    在从外边焊回来的板子中查找问题的时候,如果只是简单的 一通乱调,很有可能一下子就调好了,但是大多数的时候是调了半天,不知道接下来该如何进行,因此,严格的按照步骤走,是个不错的想法: 1.拿到板子的第一 ...

  8. Java Web学习总结(29)——Java Web中的Filter和Interceptor比较

    1. 背景 在设计web应用的时候,用户登录/注册是必不可少的功能,对用户登录信息进行验证的方法也是多种多样,大致可以认为如下模式:前端验证+后台验证.根据笔者的经验,一般会在前端进行一些例如是否输入 ...

  9. pyhthon第一个小脚本——文件备份

    先说说这个脚本的作用:对指定路径的文件进行压缩备份到另一个指定的路径,并且压缩文件的文件名用当时的日期+时间命名. 先是对着<简明Python教程>上的代码敲的,一堆错误,书上给的是lin ...

  10. JVM即时编译(JIT)

    Java解释执行过程: 代码装入-代码校验-代码执行 Java字节码的执行方式分为两种:即使编译方式和解释执行方式.即时编译是值解释器先将字节码编译成机器码,然后执行该机器码.解释执行的方式是指解释器 ...