热门标签 | 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的两个区


扫描二维码获取

更多精彩







推荐阅读
  • 揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节
    揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • 2021年7月22日上午9点至中午12点,我专注于Java的学习,重点补充了之前在视频中遗漏的多线程知识。首先,我了解了进程的概念,即程序在内存中运行时形成的一个独立执行单元。其次,学习了线程作为进程的组成部分,是进程中可并发执行的最小单位,负责处理具体的任务。此外,我还深入研究了Runnable接口的使用方法及其在多线程编程中的重要作用。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 本文详细解析了Java类加载系统的父子委托机制。在Java程序中,.java源代码文件编译后会生成对应的.class字节码文件,这些字节码文件需要通过类加载器(ClassLoader)进行加载。ClassLoader采用双亲委派模型,确保类的加载过程既高效又安全,避免了类的重复加载和潜在的安全风险。该机制在Java虚拟机中扮演着至关重要的角色,确保了类加载的一致性和可靠性。 ... [详细]
  • 线程能否先以安全方式获取对象,再进行非安全发布? ... [详细]
  • 该问题可能由守护进程配置不当引起,例如未识别的JVM选项或内存分配不足。建议检查并调整JVM参数,确保为对象堆预留足够的内存空间(至少1572864KB)。此外,还可以优化应用程序的内存使用,减少不必要的内存消耗。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 深入浅析JVM垃圾回收机制与收集器概述
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》的阅读心得进行整理,详细探讨了JVM的垃圾回收机制及其各类收集器的特点与应用场景。通过分析不同垃圾收集器的工作原理和性能表现,帮助读者深入了解JVM内存管理的核心技术,为优化Java应用程序提供实用指导。 ... [详细]
  • 初探性能优化:入门指南与实践技巧
    在编程领域,常有“尚未精通编码便急于优化”的声音。为了从性能优化的角度提升代码质量,本文将带领读者初步探索性能优化的基本概念与实践技巧。即使程序看似运行良好,数据处理效率仍有待提高,通过系统学习性能优化,能够帮助开发者编写更加高效、稳定的代码。文章不仅介绍了性能优化的基础知识,还提供了实用的调优方法和工具,帮助读者在实际项目中应用这些技术。 ... [详细]
  • 在Java面向对象编程中,内存管理是关键环节之一。Java虚拟机(JVM)的内存结构主要由栈(Stack)、堆(Heap)和方法区(Method Area)组成。其中,栈用于存储方法的执行上下文,每个方法调用时都会创建一个栈帧,用于保存局部变量表、操作数栈等信息。堆则是用于存储对象实例和数组,是垃圾回收的主要区域。方法区则用于存储类的元数据、常量池、静态变量等信息。通过合理管理和优化这些内存区域,可以显著提升程序的性能和稳定性。 ... [详细]
  • SQLite数据库CRUD操作实例分析与应用
    本文通过分析和实例演示了SQLite数据库中的CRUD(创建、读取、更新和删除)操作,详细介绍了如何在Java环境中使用Person实体类进行数据库操作。文章首先阐述了SQLite数据库的基本概念及其在移动应用开发中的重要性,然后通过具体的代码示例,逐步展示了如何实现对Person实体类的增删改查功能。此外,还讨论了常见错误及其解决方法,为开发者提供了实用的参考和指导。 ... [详细]
  • 深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用
    深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用 ... [详细]
  • 深入解析 JVM 运行时数据区域及指令集
    本文详细探讨了JVM运行时数据区域的结构及其指令集,深入分析了各个内存区域的功能与作用,并对常用指令进行了全面解析,为开发者提供了深入理解JVM工作原理的参考。通过具体示例,本文还展示了如何利用这些知识优化代码性能和调试问题。 ... [详细]
  • 深入解析 ELF 文件格式与静态链接技术
    本文详细探讨了ELF文件格式及其在静态链接过程中的应用。在C/C++代码转化为可执行文件的过程中,需经过预处理、编译、汇编和链接等关键步骤。最终生成的可执行文件不仅包含系统可识别的机器码,还遵循了严格的文件结构规范,以确保其在操作系统中的正确加载和执行。 ... [详细]
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社区 版权所有