JVM(一)——架构基础

news2024/11/20 12:41:59

JVM

在这里插入图片描述

java虚拟机

在这里插入图片描述

java gc 主要回收的是 方法区 和 堆中的内容,以下架构图是重点

方法区和堆是线程共享,java栈、本机方法栈、程序计数器是线程私有。

在这里插入图片描述

运行时数据区可以用Runtime.getRuntime()获取

字节码执行引擎,修改程序计数器,执行方法区

jvm自带插件

反汇编:先进到target,class文件存放区

javap -c Math.class > Math.txt,把字节码文件反汇编存入txt,也可以idea设置外部插件

javap -v -p Math.class直接打印汇编指令。

jvisualvm可打开java VM。识别本机所有java进程并查看这些进程的运行信息。可再安装Visual GC插件,实现查看GC情况。

类加载器ClassLoader

多线程等待cpu调用。

负责加载class文件,class文件在文件开头有特定的文件标示(class文件内容前是cafe babe),将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定,Execution Engine执行引擎负责解释命令,提交操作系统执行。

在这里插入图片描述

Class是模板,car1-3是实例

虚拟机自带加载器:

  • 启动类加载器(Bootstrap) C++语言开发, 加载JAVAHOME/jre/lib/rt.jar,原始java类
  • 扩展类加载器(Extension) Java, 加载JAVAHOME/jre/lib/ext/*.jar,后期java类,javax等
  • 应用程序类加载器(AppClassLoader),也叫系统类加载器,加载当前应用的classpath的所有类,加载用户写的类

用户自定义加载器:

Java.lang.ClassLoader的子类,用户可以定制类的加载方式,如下图虚线以下:

该类是抽象类,可继承实现。

在这里插入图片描述

代码验证

public class Hello {

    public static void main(String[] args) {
        //系统自带,系统自带无法显示自己的加载器
        Object object = new Object();
        System.out.println(object.getClass().getClassLoader());

        //自定义
        Hello hello = new Hello();
        System.out.println(hello.getClass().getClassLoader());
        System.out.println(hello.getClass().getClassLoader().getParent());
        System.out.println(hello.getClass().getClassLoader().getParent().getParent());
    }

}

结果:

null
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null

sun.misc.Launcher是jvm的程序调用入口

双亲委派

新建模拟java基础类:

package java.lang;

public class String {

    public static void main(String[] args) {
        System.out.println("Hello");
    }

}

运行main,报错如下:

在这里插入图片描述

加载类时不是从应用程序类加载器开始,java设置先加载Bootstrap源码寻找类,再加载Extension扩展源码寻找类,如果存在包名类名一致的,按优先级选择类,Bootstrap>Extension>用户自定义AppClassLoader,如果都没有,则报classNotFound异常。这样保证了java源码安全性,沙箱安全机制。

在这里插入图片描述

当一个类收到子类加载请求,他着先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自已无法完成这个请求的时候(在它的加载路没有找到所需加载的Class),类加程器才会尝试自己去加载。

采用双亲委派的一个好处是比如加载位rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。

Native Interface

实例对象在堆,引用在栈

类未实现方法不加native会报错,navtive是关键字

在这里插入图片描述

在这里插入图片描述

Native主要用来调底层的第三方C/C++语言函数库dll,有navtive标记的方法由第三方调用,加载到native method 栈中。

本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有调用C/C+程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见。因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用Web Service等等,不多做介绍。

它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。

PC程序计数器

类似汇编的cx寄存器

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。

这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令,如果执行的是一个Native方法,那这个计数器是空的(其他语言)。

用以完成分支、循环、跳转、异常处理、线程恢复等基础功能。不会发生内存溢出(OutOfMemory=OOM)错误

方法区Method Area

供各线程共享的运行时内存区域。它存储了每一个类的结构信息,类模板,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容。上面讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PermGen space)和元空间(Metaspace)。

But:

实例变量(main方法等)存在堆内存中,和方法区无关

存放类模板+常量池+静态变量

java7 new 永久代

java8 new 元空间

java栈

栈管运行,堆管存储

比如,try,catch出异常打印的是e.printStackTrace();,管理运行

栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配

栈存储什么?

栈帧(java栈中把方法压入,一个方法就是一个栈帧)中主要保存3类数据:

  • 本地变量(Local Variables):输入参数和输出参数以及方法内的变量。
  • 栈操作(Operand Stack):记录出栈、入栈的操作。
  • 栈帧数据(Frame Data):包括类文件、方法等等。

在这里插入图片描述

栈运行原理:

栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,

A方法又调用了B方法,于是产生栈帧F2也被压入栈,

B方法又调用了C方法,于是产生栈帧F3也被压入栈,

执行完毕后,先弹出F3栈帧,再弹出F2栈帧,再弹出F1栈帧……

遵循“先进后出” / “后进先出”原则。

每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。栈的大小和具体JVM的实现有关,通常在256K~756之间,等于1MB左右。

在这里插入图片描述

在这里插入图片描述

递归未中断的话报错误(Error),不是异常,栈溢出错误,一直不断放m1方法到栈

在这里插入图片描述

java栈有:局部变量表、操作数栈、动态链接,方法出口。

操作数栈存放值(临时内存,存放值),后赋值给局部变量表。

动态链接是一个将符号引用解析为直接引用的过程,那么虚拟机就必须解析这个符号引用。在解析时,虚拟机执行两个基本任务

1.查找被引用的类,(如果必要的话就装载它)

2.将符号引用替换为直接引用,这样当它以后再次遇到相同的引用时,它就可以立即使用这个直接引用,而不必花时间再次解析这个符号引用了。

方法出口指方法中执行完其他方法,后可以回来从刚离开的指令处继续执行。

栈+堆+方法区的交互关系

在这里插入图片描述

HotSpot(jdk名字)是使用指针的方式来访问对象。

Java堆中会存放访问类元数据的地址。

reference存储的就直接是对象的地址。

堆heap

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:

  • Young Generation Space 新生区 Young/New
  • Tenure generation Space 养老区 old/Tenure
  • Permanent Space 永久区/元空间 Perm

java7之前:

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。

在这里插入图片描述

物理上:新生+养老

简单流程

java8:

新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分:伊甸区(Eden Space)和幸存者区(Survivor Space),所有的类都是在伊甸区被new出来的。幸存区有两个:0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,执行引擎执行gc对该区进行垃圾回收,然后移动到1区。那如果1区也满了呢?再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC),进行养老区的内存清理。若养老区执行了FullGC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”,叫堆溢出异常

如果出现iava,lang.OutofMemoryError:Java heap space异常,说明lava虚拟机的堆内存不够。原因有二:

  • java出拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
  • 代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)

实际而言,方法区(Method Area)和堆一样,是各个线程共享的内在区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还一个别名叫做Non-Heap(非堆),目的就是要和堆分开。对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)”,但严格本质上说两者不同,或者说使用永久代来实现方法区而己,永久代是方法区(相当于是一个接口interface)的一个实现,jdk1.7的版本中,已经将原本放在永久代的宁符串常量池移走。永久存储都是存储rt等永久的类数据。

永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM会释放此区域所占用的内存。

在这里插入图片描述

java7:

在这里插入图片描述

java8:

在这里插入图片描述

在Java8中,永久代已经被移除,被一个称为元空间的区域所取代。元空间的本质和永久代类似。

元空间与永久代之间最大的区别在于:
永久带使用的VM的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存。

因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

常见的垃圾回收算法

GC分代收集算法、

JVM在进行GC时,并非每次都对新旧区元空间内存区域一起回收的,大部分时候都是指新生区。

因此GC按照回收的区域又分了两种类型。一种是普通GC(minor GC),一种是全局GC(major GC for Full GC)

minor GC和major GC for Full GC

  • 普通GC,minor GC:只针对新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数java对象存活率都不高,所以普通GC非常频繁,一般回收速度也比较快。

  • 全局GC,major GC for Full GC:指发生在老年代的垃圾收集动作,出现了全局GC,经常会伴随至少一次的全局GC(不是绝对),全局GC的速度要比普通GC慢上10倍以上。

  • 引用计数法

在这里插入图片描述

在双端循环,互相引用的时候,容易报错,目前很少使用这种方式了。缺点计数耗费内存,无法解决循环引用。

循环引用:

objectA.instance = objectB;
objectB.instance = objectA;

System.gc();//手动gc
  • 复制Copying

新生代使用minor GC,老年代使用major GC for Full GC

复制算法在年轻代的时候,进行使用,复制时候有交换

幸存区0、幸存区1位置和名分没有区分,不是固定的,只是名字而已 ,都可以是From和To区

谁空谁是To区,GC第一次交换找TO区,TO区有数据变From区,TO和From交换区名,动态交换,每次GC交换区。新创建的对象都会被分配到Eden区,这些对象经过第一次Minor GC后,如果仍然存活,将会移到Survivor区。对象在Survivor区每熬过一次Minor GC,年龄就会增加1岁,默认增加到15岁移动到老年代中。由于年轻代基本90%都是未重复的类,所以年轻代的垃圾回收算法使用的是复制算法,将内存分为两块,每次用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上,复制算法不会产生内存碎片。

过程:

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制词“To”,而在“From"中,扔存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshod来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空,这个时候,“From”到“to”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到"To”区被填满,“To"区被填满之后,会将所有对象移动到年老代。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

优点:没有产生内存碎片

缺点:浪费幸存区一半的内存,如果对象存活率很高,那需要将所有对象复制一遍,并将所有引用地址重置一遍,耗时。

  • 标记清除Mark-Sweep

老年代一般是由标记清除和标记整理混合实现的

先标记,后清除,缺点是会产生内存碎片,用于老年代多一些

在这里插入图片描述

缺点是两次扫描,耗时严重,会产生内存碎片

当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行。

  • 标记整理Mark-Compact

标记,然后整理对象,不存在内存碎片

在这里插入图片描述

但是需要付出代价,因为移动对象需要成本

新生代复制算法,对象存活率低

老年代用标记清除与标记整理混合实现,对象存活率高

内存效率:复制算法=标记清除算法>标记整理算法。
内存整齐率:复制算法=标记整理算法>标记清除算法。
内存利用率:标记整理算法=标记清除算法>复制算法。

调优例子

对象动态年龄判断机制:一批对象大于From幸存者内存50%,直接存入老年代。

在这里插入图片描述

减少Full GC调优:

java -Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -jar java.jar

Xms堆起始大小,-Xmx堆最大大小,-Xmn年轻代大小,-Xss一个线程java栈大小,XX:MetaspaceSize元空间,即方法区大小,-XX:MaxMetaspaceSize最大方法区大小。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不要怀疑了,个人也是可以做好跨境电商的!

近几年随着跨境电商卖家们赚得盆满钵满,许多人都想从中分一杯羹,进而入住了跨境电商市场,有人与一些公司企业合作,也有人选择了自己做跨境电商平台,个人做的优劣势又有哪些呢? 个人做跨境电商平台最明显突…

A V L树

概念 在之前介绍了搜索二叉树,但是当我们插入的数据若是有序或者接近于有序,那么此时查找的效率底下,于是俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后&am…

javaEE学生教学实习计划申报系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 javaEE学生教学实习计划申报系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql&…

正点原子【第四期】手把手教你学 Linux之驱动开发篇-01

学习目的 了解驱动开发和应用开发的过程,具有一定的基础就行 第一讲:linux驱动开发与裸机开发区别 刚开始听不懂很正常,等之后学了一点你就会知道它说啥了 第二讲:字符设备驱动开发基础 字符设备驱动是最简单的,块设…

C++ 移动语义

从拷贝说起我们知道,C中有拷贝构造函数和拷贝赋值运算符。那既然是拷贝,听上去就是开销很大的操作。没错,所谓拷贝,就是申请一块新的内存空间,然后将数据复制到新的内存空间中。如果一个对象中都是一些基本类型的数据的…

【Unity3D】绘制物体外框线条盒子

1 需求描述 点选物体、框选物体、绘制外边框 中介绍了物体投影到屏幕上的二维外框绘制方法,本文将介绍物体外框线条盒子绘制方法。 内框:选中物体后,绘制物体的内框(紧贴物体、并与物体姿态一致的内框盒子)外框&#…

Python与C++语法比较--字符串篇

tags: C Python 写在前面 刷lc从Python转向C, 不只是语法层面, 还要改变很多的API, 这次记录一下C和Python在字符串方面的一些区别, 供参考. 基本区别 Python字符串是不可变类型, 而C的String为容器(本质上是一个类的别名, 说容器有点不合适, 因为容器内的元素类型已经被指…

Kotlin中的Lambda编程

文章目录1.集合的创建与遍历2.集合的函数式API3.Java函数式API的使用1.集合的创建与遍历 传统意义上的集合主要是List和Set,再广泛一点的话,像Map这样的键值对数据结构也可以包含进来。List,Set和Map再Java中都是接口,List主要的…

Java设计模式-命令模式Command

介绍 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收 者是谁,也不知道被请求的操作是哪个, 我们只需在程序运行时指定具体的请求接收者即可&…

数影周报:TikTok因在线跟踪被罚500万欧,Windows 7退出历史舞台

本周看点:TikTok因在线跟踪被法国罚款500万欧元 ;思科已裁员近700 人;Windows 7退出历史舞台;亚马逊向所有卖家开放Buy with Prime服务;“全路程”完成2亿元C轮融资......数据安全那些事TikTok因在线跟踪被法国罚款500…

Android13 wifi无线调试adb连接设置

在进行adb调试的时候,有时候需要使用wifi连接,或者wifi连接较为方便,早些的Android上,需要设置端口等操作,adb tcpip 6666参考android wifi adb 调试 - 简书 (jianshu.com)好几步操作,在Android13上&#x…

Deque 的理解 STL中stack与queue为什么选择使用deque为底层模板容器

目录 一、Deque的引入 二、Deque是什么? 三、deque的遍历方式?deque的缺陷? 四、它为什么能更贴合与stack与queue? 五、STL中vector与list的底层实现 一、Deque的引入 Stack、Queue在之前的博客中我也是分别使用了更容易处理…

【蓝桥杯】历届真题 杨辉三角形 (省赛)Java

【问题描述】 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列: 1,1,1,1,2,1,1,3,3,1,1,4,6,4,1&…

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 (六)多功能数据处理器

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 (六)多功能数据处理器 🔈声明: 😃博主主页:王_嘻嘻的CSDN博客 🧨未经作者允许,禁止转载 🔑系列专栏:牛客…

react基础Day02-受控组件评论案例propscontext

React组件 目标 能够知道受控组件是什么能够写出受控组件了解非受控组件 表单处理 受控组件(★★★) HTML中的表单元素是可输入的,也就是有自己的可变状态而React中可变状态通常保存在state中,并且只能通过setState() 方法来…

[acwing周赛复盘] 第 86 场周赛20230114

[acwing周赛复盘] 第 86 场周赛20230114 一、本周周赛总结二、 4794. 健身1. 题目描述2. 思路分析3. 代码实现三、4795. 安全区域1. 题目描述2. 思路分析3. 代码实现四、4796. 删除序列1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 去吃羊蝎子了&#xff0…

基于汽车知识图谱的汽车问答多轮对话系统 详细教程

结果: 1 技术路线 该技术路线主要将KBQA分为三部分,实体识别与实体链接,关系识别,sparql查询,其中每个部分分为一到多种方法实现。具体的处理流程图如下:

大脑的记忆

AI神经网络中的记忆 当前AI发展进入一个瓶颈,大家都意识到还是要继续在人脑中获取AI方向的指引。当然也有科学家说物理世界与心理世界并非一一对应,人类的智能也没必要与物理世界一一对应,甚至本质上都可以是不同的,所以没必要研究大脑认知和大脑的机制,更不需要分子级别…

IDEA structure窗口各标志及功能

文章目录图标对象类型访问权限其他修饰符工具栏图标 对象类型 class 类 interface 接口 enum 枚举 interface 注解 class initializer 代码块 method 方法 field 字段/属性 anonymous class 匿名类 lambda lambda表达式 propertie 访问器(get方法&#xff0…

【Java面试】Queue接口

文章目录BlockingQueue中有哪些方法,为什么这样设计?BlockingQueue是怎么实现的?BlockingQueue中有哪些方法,为什么这样设计? 先看一眼结构,再看具体的分析 为了应对不同的业务场景,Blockin…