来自:非科班的科班

本文脑图

运行时数据区模型

在java虚拟机中把内存分为若干个不同的数据区域。这些区域有各自的用途,有些区域随着虚拟机进程启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。在JVM中主要分为以下几个区域:

  1. 程序计数器

  2. 方法区

  3. 虚拟机栈

  4. 本地方法栈

  5. java堆

在这里插入图片描述

程序计数器

程序计数器是内存中较小的一部分区域,是当前线程执行的字节码的行号指示器。在字节码解释器工作时通过计数器的值来选取下一条指令。

为什么需要计数器?
多线程情况下,一条线程中有多个指令,为了使线程切换可以恢复到正确执行位置,每个线程都具有各自独立的程序计数器,所以该区域是非线程共享的内存区域。

如果执行的是Java方法,计数器记录的是正在执行的字节码指令的地址;若执行的是Native方法,计数器存储为空。这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域。

我们可以得出程序计数器主要有两个作用:

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

  2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

方法区

方法区也称"永久代",它用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,是各个线程共享的内存区域。

在JDK8之前的HotSpot JVM,存放这些”永久的”的区域叫做“永久代(permanent generation)”。永久代是一片连续的堆空间,在JVM启动之前通过在命令行设置参数-XX:MaxPermSize来设定永久代最大可分配的内存空间,默认大小是64M(64位JVM默认是85M)。

方法区或永生代相关设置

  • -XX:PermSize=64MB 最小尺寸,初始分配

  • -XX:MaxPermSize=256MB 最大允许分配尺寸,

  • 按需分配XX:+CMSClassUnloadingEnabled

  • -XX:+CMSPermGenSweepingEnabled 设置垃圾不回收

  • -server选项下默认MaxPermSize为64m-client选项下默认MaxPermSize为32m

java虚拟机规范堆方法区限制非常的宽松,可以不选择垃圾回收,以及不需要连续的内存和可扩展的大小。这个区域主要是针对于常量池的回收以及对类型的卸载,当方法区无法分配到足够的内存的时候也会抛出OOM。

虚拟机栈

虚拟机栈是每个java方法的内存模型:每个方法被执行的时候都会创建一个"栈帧",用于存储局部变量表(包括参数)、操作栈、方法出口等信息。每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

平时说的栈一般指局部变量表部分。栈帧对应的结构图,如下图所示:


局部变量表所需要的空间在编译期完成分配,当执行一个方法时,该方法需要在栈帧中分配多大的局部变量表的空间完全是可以确定的,因此在方法运行的期间不会改变局部变量表的大小。

初级程序员可能笼统的将Java内存分为堆内存栈内存时,该区域就是常说的栈内存,该区域的局部变量表存放基本类型、对象的引用类型,在对象的引用类型中存储的是指向对象的地址

该区域会出现两种异常

  1. 当线程请求的栈深度大于虚拟机所允许的深度,就会抛出StackOverflowError异常。

  2. 一般虚拟机的内存都是动态扩展的,但是有可能动态的扩展还是配不到足够的内存,就会抛出OOM异常。

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务。

本地方法栈为虚拟机使用到的native方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。

Java堆

Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。

此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。堆内存是所有线程共有的,可以分为两个部分:年轻代老年代

注意:它是所有线程共享的,它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆。根据虚拟机规范,Java堆可以存在物理上不连续的内存空间,就像磁盘空间只要逻辑是连续的即可。它的内存大小可以设为固定大小,也可以扩展。当前主流的虚拟机如HotPot都能按扩展实现(通过设置 -Xmx-Xms),如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误(OutOfMemoryError)

新生代又分为:Eden空间、From SurvivorTo Survivor空间。进一步划分的目的是更好地回收内存,或者更快地分配内存。

分代回收的原因:对象的生命周期不同,所以针对不同生命周期的对象可以采取不同的回收方式,以便提高回收效率。从内存分配的角度来看,线程共享的java堆中可能会划分出多个线程私有的分配缓冲区

垃圾回收算法

标记-清除

标记-清除是最基本的回收算法,后面的算法的设计的思想都是基于此算法进行设计。标记-清除算法一共分为两个阶段标记清除阶段,标记阶段是将不可达的对象(即为不存活的对象)进行标记,接着清除阶段将这些标记的对象进行清除。


标记-清除算法主要有两个问题:一个是效率的问题,标记和清除两个过程效率都不高;另一个问题就是该算法会产生很多的内存碎片,大量的不连续内存空间,当程序需要申请较大的内存空间存储大对象的时候,有可能无法申请到足够的内存空间而不得不再一次触发一次垃圾回收动作。

复制算法

复制算法将内存划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中一个区间(称为活动区间),而另外一个区间(称为空闲区间)则是空闲的。

当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。

此时,空闲区间已经与活动区间交换,而垃圾对象现在已经全部留在了原来的活动区间,也就是现在的空闲区间。事实上,在活动区间转换为空间区间的同时,垃圾对象已经被一次性全部回收。

新生代中因为对象都是"朝生夕死的",深入理解JVM虚拟机上说98%的对象存活率很低,适用于复制算法,复制算法比较适合用于存活率低的内存区域。它优化了标记/清除算法的效率和内存碎片问题。

JVM不是平分内存,新生代中由于存活率低,不需要复制保留那么大的区域造成空间上的浪费,因此不需要按1:1划分内存区域,而是将内存分为一块Eden空间和From Survivor、To Survivor【保留空间】。

(1)新生代:大多数对象在新生代中被创建,其中很多对象的生命周期很短。每次新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。

在新生代内存中每次只是用Eden和其中的一个S区。当Eden区满时,还存活的对象将被复制到两个Survivor区中的一个。当这个Survivor区满时,此区的存活且不满足“晋升”条件的对象将被复制到另外一个Survivor区。

对象每经历一次Minor GC,年龄加1,达到“晋升年龄阈值”后,被放到老年代,这个过程也称为“晋升”。显然,“晋升年龄阈值”的大小直接影响着对象在新生代中的停留时间,在SerialParNew GC两种回收器中,“晋升年龄阈值”通过参数MaxTenuringThreshold设定,默认值为15。

(2)老年代:在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代,该区域中对象存活率高。老年代的垃圾回收(又称Major GC)通常使用标记-清理标记-整理算法。整堆包括新生代和老年代的垃圾回收称为Full GC

(3)永久代:主要存放元数据,例如ClassMethod的元信息,与垃圾回收要回收的Java对象关系不大。相对于新生代和年老代来说,该区域的划分对垃圾回收影响比较小。在 JDK 1.8中移除整个永久代,取而代之的是一个叫元空间的区域。

默认的Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即:Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

在新生代中,并不是每次存活的对象都少于10%,有时候若是存活的对象大于10%,就会向老年代进行空间分配担保

标记-整理

标记-整理算法也分为两步,首先标记不可达的对象,然后存活的对象往一端移动,然后直接清理掉端边界以外的内存。

老年代中存活率比较高,要是使用复制算法,会大量浪费时间在复制对象上,因此复制算法不适合用在存活率比较高的场景。


标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。

不难看出,标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。

不过任何算法都会有其缺点,标记/整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,标记/整理算法要低于复制算法

关于JVM深入研究以及JVM调优,后面的文章会继续深入,这篇文章先介绍JVM的内存模型,以及回收算法。

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

还在学JVM?我都帮你总结好了(附脑图)相关推荐

  1. 都快2022年了GraphQL还值得学吗?

    GraphQL感觉前两年火过一小段时间,说是能让前端只取我所需,解决后端接口过多划分地过细的问题,再加上是Facebook出品确实吸引了一些开发者的关注,不过我还没来得及学,好像它就过气了. 那么Gr ...

  2. 有了这6个东西之后,学Python还愁学不成?楼下大爷都入门了

    有人学Python学了两三个月还入不了门,而有的人却能在一个月左右就能开始自己动手做项目了,我当初学Python的时候也就只用了1个多月就能把爬虫和数据分析摸得熟门熟路,当然了,我是老程序员,学其他语 ...

  3. C语言已经自学完了,数电模电电路都还没学,学stm32会有阻碍吗

    C语言已经自学完了,数电模电电路都还没学,学stm32会有阻碍吗 起初就是学个c语言,跟着郭天祥视频进修,一通开发.大一的电路知识仅限于欧姆定律,电容滤波,二极管正向导通这样吧.但也搞得红红火火地,感 ...

  4. php表白情话,情人节这些表白文案都帮你准备好了,就怕你还没对象(捂脸)

    原标题:情人节这些表白文案都帮你准备好了,就怕你还没对象(捂脸) 春节一过完 情人节就要来了 还没脱离上班的噩耗 又要被狗粮投喂了 都忙着过年.撕春晚 忘记2.14情人节了吧? 莫慌,广告君这就 为你 ...

  5. 看完一个在校大学生的 Java 学习历程,我觉得我还能学得更多

    不知道出处,是在qq空间看到的,觉得很有道理,原文作者看到了请评论附上原文链接,我来添加上 写在前面: 其实学习是一件很私人的事情,每个人都应该有一套自己的学习方式,而不是照搬照抄别人的.适合别人的不 ...

  6. 看完一个在校大学生的Java学习历程,我觉得我还能学得更多

    GitChat 作者:MyStery 原文:程序员如何高效学习(以 Java 为例) 关注微信公众号:「GitChat 技术杂谈」 一本正经的讲技术 写在前面:其实学习是一件很私人的事情,每个人都应该 ...

  7. python对学历有要求嘛_学好python和photoshop好就业吗,对学历有什么要求吗??或者还需要学什么才能更好的就业。。...

    学好python和photoshop好就业吗,对学历有什么要求吗??或者还需要学什么才能更好的就业.. 其实你现在考虑这个问题有点早,毕竟才高二. 如果学设计,要有美术的基础:学计算机不是学用软件,而 ...

  8. 现在学java的都是傻子

    不经意的看见,看到学java的都是傻子.当不经意看到,说明,这个最近已经在网上疯传了很多.说明目前这个行业真的已经不好了.所以你得自己当心了.在这个行业不知有多少学习了又放弃了.博主我也是其中之一,从 ...

  9. python学了可以干什么-学了Python都能干什么,哪个最赚钱?

    原标题:学了Python都能干什么,哪个最赚钱? Python有哪些应用领域?这同时也关乎着你的就业和薪资.我们今天就来看看Python的主要开发领域,最后,为大家附上每个岗位的薪资. 胶水语言Pyt ...

最新文章

  1. 外媒分析:iPhone销量低于预期是中国市场疲软影响的
  2. SpringBoot 基础上传操作
  3. Java秒杀系统实战系列~RabbitMQ死信队列处理超时未支付的订单(转)
  4. 云顶之弈机器人法爆_LOL云顶之弈机器人出装怎么选
  5. spring-retry小结
  6. 在服务器端运行JavaScript文件(二)
  7. Android 4.0 ICS SystemUI浅析——SystemUI启动流程
  8. perl中的文件句柄
  9. jQuery操作属性的相关方法
  10. android 获取路由器mac,android设备获取当前wifi下的路由器的mac和路由器的名称
  11. 图解 Cisco IOS 命名规范
  12. latex中页眉怎么去掉_LaTeX页面布局专题——页眉和页脚
  13. 拼多多出现重大BUG,几小时内损失超千万,但处理方式让用户怒了
  14. IPQ5000/IPQ5010/IPQ5018/方案WiFi6开发 工业5G CPE
  15. 预训练模型 PLOME
  16. 如何在Windows 10上压缩(和解压缩)文件
  17. m0n0wall 软件防火墙 虚拟机中 安装与配置
  18. Unity:锚点详解
  19. 半导体显示丨深天马A拟投资15亿元在武汉设立新型显示产业创新中心
  20. Java script 教程

热门文章

  1. 迁移学习之DenseNet121(121层),DenseNet169(169层),DenseNet201(201层)(图像识别)
  2. HDU3183(ST表)
  3. poj2367拓扑排序模版题
  4. java treemap get_java treeMap 排序后 get不到value
  5. jsapi设计_一个简单API设计
  6. php鼠标已入移除,angularjs鼠标移入移出实现显示隐藏
  7. oracle solaris 内核 源码,Oracle和SUN Solaris内核参数
  8. P1781 宇宙总统
  9. python tqdm 不换行_python tqdm 实现滚动条不上下滚动代码(保持一行内滚动)
  10. opencvsharp 如何提取十字交叉点坐标_如何提取OpenFOAM计算结果的体心坐标