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. find命令查找和替换

    find命令查找和替换 语法: find -name '要查找的文件名' | xargs perl -pi -e 's|被替换的字符串|替换后的字符串|g' #查找替换当前目录下包含字符串并进行替换 ...

  2. 「 HDU P3555 」 Bomb

    # 题目大意 给出 $\text{T}$ 个数,求 $[1,n]$ 中含 ‘49’ 的数的个数. # 解题思路 求出不含 '49' 的数的个数,用总数减去就是答案. 数位 $DP$,用记忆化来做. 设 ...

  3. 「 hihoCoder 1014 」Trie 树

    标题真直接 题目大意 给你 $n$ 个字符串.存到一个字典中.又给你 $m$ 个询问,每个询问给一个字符串,在字典中查出有多少个字符串是以这个字符串为前缀. 解题思路 模板题啊 在每个点设置一个变量 ...

  4. 利用WebUploader进行图片批量上传,在页面显示后选择多张图片压缩至指定路径【java】

    WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件.在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览 ...

  5. Servlet过滤器的使用

    Servlet过滤器的使用 制作人:全心全意 Servlet过滤器:Servlet过滤器与Servlet十分相似,但它具有拦截客户端请求的功能,Servlet过滤器可以改变请求中的内容,来满足实际开发 ...

  6. 如何用纯 CSS 创作一个按钮文字滑动特效

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. 在线预览 https://codepen.io/zhang-ou/pen/GdpPLE 可交互视频教 ...

  7. redis+php+mysql处理高并发实例

    一.实验环境ubuntu.php.apache或nginx.mysql二.利用Redis锁解决高并发问题,需求现在有一个接口可能会出现并发量比较大的情况,这个接口使用php写的,做的功能是接收 用户的 ...

  8. LINUX-用户和群组

    groupadd group_name 创建一个新用户组 groupdel group_name 删除一个用户组 groupmod -n new_group_name old_group_name 重 ...

  9. rsync+sersync自动同步备份数据

    一.为什么要用Rsync+sersync架构?1.sersync是基于Inotify开发的,类似于Inotify-tools的工具2.sersync可以记录下被监听目录中发生变化的(包括增加.删除.修 ...

  10. POJ - 2007 极角排序(Java 实现)

    POJ 2007 将所有的点按逆时针输出 import java.io.*; import java.util.*; public class Main { static class Point im ...