热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一篇了解JVM

JVM一个在Java程序背后默默工作的人,任劳任怨,有实际使用情况




点击上方蓝字关注我们






JVM一个在Java程序背后默默工作的人,任劳任怨,有实际使用情况中有时需要针对程序对JVM进行一些设置,进行一些处理,而这也是本文要说的如果需要对JVM进行调优工作,我们首先需要了解JVM内存机制以及垃圾回收的机制



JVM中内存



jvm的内存图如下

程序计数器



内存空间小,线程私有。改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成

Java 虚拟机栈



栈,数据暂时存储的位置,先进后出的原则存储数据,线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表
操作数栈
动态链接
返回地址
,个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。

局部变量表:主要保存函数的参数以及局部的变量信息

操作数栈:一个后入先出栈,属于方法执行时的计算区域, 方法计算时字节码指令往操作数栈中执行入栈和出栈操作

动态链接:指向运行时常量池中该栈帧所属性方法的引用,持有这个引用是为了支持方法调用过程中的动态连接

返回地址:保存当前帧栈,方便恢复上层方法执行状态

本地方法栈

为本地方法提供服务,其中本地方法指的是操作计算机底层的一些代码,这部分代码并非使用Java编写,存储在本地

java堆



JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。 由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。

方法区/永久代



线程共享区域,存储包括类信息,类型常量池,字段信息,方法信息,类变量(也就是静态变量),类加载器信息,指向Class实例的引用,方法表

这里的类型常量池指的是类里面的常量,在类加载的时候会把把里面的内容放到方法区中的运行时常量池

在jdk7之前,HotSpot虚拟机使用的是永久代,方法区和永久代有本质上的不同,方法区是JVM标准,而永久代是JVM规范中的具体实现,并且只有HotSpot才拥有永久代,其他虚拟机没有。而在1.8版本中,移除了永久代,具体点说就是

JDK8 HotSpot JVM 移除永久代,使用本地内存来存储类元数据信息并称之为:元空间(Metaspace)

两者最大区别在于:元空间使用的是本地内存而不是JVM虚拟机。

另外注意在原来的JDK7这个版本中字符串常量池原本在永久代中,但是在后面JDK8中永久代被移除,所以这个时候常量池这个东西也就是从方法区移动到了Java堆中,这里注意注意:这里说的是字符串常量池,运行常量池包含字符串常量池,但是还有其他部分,1.8后字符串常量池移动到堆中,其他的还是在原地


一个对象的创建



在我们了解到JVM后,接下来我们说下是怎么运行这些内存的,创建一个对象步骤如下

1、当虚拟机收到一条new指令时,首先将检查当前new的类是否在常量池被加载过(在常量池找到需要new的类的符号,检查其是否被初始化过)。如果没有,说明类没有加载过,则执行相应的类加载过程;如果有则直接准备为新的对象分配内存。

2、把计算后的内存大小从内存中划取出来,内存的分配有两种

指针碰撞:

想象一个内存空间:左侧是已使用的内存区域,右侧是空闲内存区域,中间是指针来作为分界点的指示器。当我们分配内存时,只需要指针向右移动与对象所需内存相同大小的距离即可,所以称为“指针碰撞”

空闲列表:

当空闲内存区域和已使用内存区域相互交错时,虚拟机就需要维护一个列表,用来记录空闲内存块。当需要分配内存的时候,则需要找到一块足够大的内存区域分给对象实例,并更新表中的记录

3、线程安全问题,当在多线程情况下创建对象可能造成意外情况,所以这时候使用的不是上面的两种,而是新的方法,

4、初始化内存空间,把分配后的内存空间中的值初始化为0

5、设置对象必要参数,类的元数据信息,对象的hash码,GC年龄,锁相关

6、最后执行对象构造函数

简单点说流程就是:

检查有没有 ---》    开始分配内存 ----》 初始化值为零 ----》 添加对象必要参数 ----》 执行构造函数



垃圾回收机制


垃圾回收(GC)机制,JVM中一个面试以及工作中常看到的值,垃圾回收机制回收的就是不用的对象,以此来释放内存,增加软件的健壮性

3-1 标记对象



垃圾回收机制主要的操作区域就是堆和方法区,这里面如果要进行处理的话就需要先找到需要回收的对象,这里面主要使用发方法有

引用计数算法:堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1

可达性算法:程序把所有的引用关系看作一张图,从一个节点GC ROOT(根目录)开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。在Java中,虚拟机栈引用的对象,方法区中静态属性引用的变量,常量引用的对象,本地方法引用的对象不能作为GC ROOT

这里面的引用共分为强引用,软引用,弱引用,虚引用,引用的强度逐渐减弱

强引用:普遍存在的,直接通过new创建的,GC不会回收被引用的对象

软引用:还有用但并非必须的对象,在内存溢出异常之前会把这些对象进行回收

弱引用:描述非必须对象,只生存到下次垃圾收集发生前

虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。它的作用是能在这个对象被收集器回收时收到一个系统通知。

3-2 开始回收

标记-清除算法

先扫描,对存活的对象进行标记,标记完成后扫描整个空间,然后回收没有标记的对象。好处是回收方便,坏处是会造成内存碎片

复制算法

为解决内存碎片的问题,把堆分成一个对象面或者多个 空闲面, 程序从对象面为对象分配空间,当对象满了,收集就从根集合(GC Roots)中扫描活动对象,并将每个 活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存

标记-整理算法

类似于标记清除算法,不同的是回收后会把剩余存活的对象那个向空闲空间移动,并更新指针,以此来解决内存碎片问题,但是会提高回收成本

分代回收算法

根据对象存活的声明周期把内存分为若干个区域,在JVM中堆分为两个区域:新生代,老年代。

其中新生代分为三块区域:Eden、From Survivor、To Survivor ,之间的比例是8:1:1 。

大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。

当survivor1区不足以存放 eden和survivor0的存活对象时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。

老年代,可以理解经过多次垃圾回收仍然存在的对象,当老年代存满的时候出发Full GC

这里的Full GC 指的是对整个堆进行处理回收,共有四种情况导致Full GC

a) 年老代(Tenured)被写满;

b) 持久代(Perm)被写满;

c) System.gc()被显示调用;

d) 上一次GC之后Heap的各域分配策略动态变化;

除了Full GC出发之外还有一种执行,就是 Scavenge GC , Scavenge GC 是在Eden申请空间失败时触发,对Eden进行GC,清除非存活对象,并且把存活的对象移动到Survivor区。然后整理Survivor的两个区


扫描二维码获取

更多精彩







推荐阅读
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • 本文详细介绍了相机防抖的设置方法和使用技巧,包括索尼防抖设置、VR和Stabilizer档位的选择、机身菜单设置等。同时解释了相机防抖的原理,包括电子防抖和光学防抖的区别,以及它们对画质细节的影响。此外,还提到了一些运动相机的防抖方法,如大疆的Osmo Action的Rock Steady技术。通过本文,你将更好地理解相机防抖的重要性和使用技巧,提高拍摄体验。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • Java编程思想一书中第21章并发中关于线程间协作的一节中有个关于汽车打蜡与抛光的小例子(原书的704页)。这个例子主要展示的是两个线程如何通过wait ... [详细]
  • 老牌医药收割AI红利:先投个15亿美元抢中国人才
    萧箫发自凹非寺量子位报道|公众号QbitAI没想到,一场大会把我的“刻板印象”攻破了。2021世界人工智能大会现场,能看见不少熟悉的身影, ... [详细]
  • TigerGraph101系列课程四直播报名及往期回顾
    报名 ... [详细]
author-avatar
小乐的孤独人生_298
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有