java JVM总览

在这里插入图片描述
在这里插入图片描述
左边是java的开发环境,右边是java的运行环境。
右边:java会把java字节码文件,通过Java类装载器,装载在虚拟机上。当装载过程中,也会对字节码文件进行认证。比如说,认证你这个字节码文件是不是格式正确啊。在转载过程中也会转载一些Java类库。(也就是说,必须要由虚拟机提供的功能,也必须通过虚拟机的类装载器,在这个过程进行装载)
当把这些需要的东西转载过后,虚拟机会把这些东西交给java解释器。java解释器会对咱们的字节码进行解释并执行。进行运行期等。当然这个区间会很麻烦。
在这里插入图片描述
这就是java虚拟机提供的java class文件的运行环境。在往下就是操作系统了,出虚拟机了。由虚拟机和操作系统进行操作了。
在这里插入图片描述
在这里插入图片描述
实际上,java是不和硬件打交道的,java和虚拟机进行交换,然后,虚拟机和硬件打交道。
在这里插入图片描述
第一部分,第二部分,是我们自己开发的,然后class文件被加载到虚拟机,其中与平台相关的操作是交给虚拟机来进行。比如说,不同平台的虚拟机版本是不同的。
比如说我们不会对windows linux 平台写不一样的java程序,我们都是写一套。然后交给虚拟机,由虚拟机和平台进行打交道。至于虚拟机在那个平台上,我们就不管了。
在这里插入图片描述
java源程序可以通过javac编译,生成class文件,但是,不一定只有java生成的class文件可以被虚拟机装载。其实,只要是你能生成class文件即可。不管是jRuby JPython,只要能生成class文件,他都可以被装载进入虚拟机中。甚至,你都可以自己写Class文件,只要符合规范都可以转载进入虚拟机。虚拟机认识的只是class文件。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Class
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


JVM学习诸葛
java跨平台:就是写了一份代码 可以在不同的操作系统中进行运行;
我们在下载JDK的时候,我们会选择不同的版本,比如说:windows linux等;
选取了不同的JDK会针对不同的平台JVM有不同的实现,会编译成不同的01指令,让计算机去执行;
由于,我们的代码运行在不同平台的JDK版本上时,其生成的机器码是不同的;所以,才是实现跨平台的原因

JVM整体架构

在这里插入图片描述
宏观层面:class文件,1.先通过类装载子系统装载到运行时数据区(内存模型),2.通过字节码执行引擎去执行class文件;
1.(虚拟机)栈:栈可以理解为线程栈,当我们运行一个线程时,需要存储一些局部变量;这些局部变量是存放在栈中的;所以,在Java程序运行时,会先分一小块内存区域(栈),这一小块内存区域是用来存放这些局部变量的;
栈是用来放局部变量的,那么他里面是怎么放局部变量的呢?他里面是通过栈帧来放局部变量的(每个栈帧中有一张局部变量表);
栈内部会存放很多栈帧,那么栈帧是怎么一回事?不同的方法的局部变量只在自己方法中有效;所以,每个方法运行时,虚拟机就会给该方法分配一块栈帧,这个栈帧就用来放自己的局部变量;
在这里插入图片描述
调用一个方法,就会压一个栈帧进去;
当一个方法调用完,它对应的那块栈帧就会销毁掉;这时候,栈帧对应的局部变量就会销毁掉;
这也是,方法先调用 后销毁 ; 后调用,先销毁;
栈内部最重要是存储局部变量,但是:其内部还有,操作数栈、动态链接、方法出口的功能;

java指令码:javap -c xxx.class > xxx.txt 反汇编 生成可读字节码
在这里插入图片描述
这时候,就会好看多了;就可以看到指令码了,就可以知道JVM的运行情况了;
操作数栈:在方法中,需要计算时,就是使用操作数栈;比如说 1 + 2 = 3 操作数栈先压1 再压入 2 然后,把它们弹出来,结果为3,存放临时的操作数,到时候需要赋值回变量的;
方法出口:解决当该方法运行完毕以后,应该回到主方法的什么位置;存放,主方法中的行号;

2.程序计数器 :记录当前线程正在运行的那行JVM指令码的行号;每个线程独有的;
**为什么需要程序计数器呢?**该线程在运行的过程中,如果CPU时间片被别的线程抢走了;该线程就需要挂起;所以,就需要程序计数器去记录当前是执行到那一行了;

3.方法区:是各个线程共享的内存区域,存放 常量 + 静态变量 + 类信息;(类名、访问修饰符、常量池、字段描述、方法描述);
首先:将class文件加载到方法区,然后,字节码执行引擎,去方法区进行执行;方法区是各个线程共享的内存区域。而对于一个类来说:常量 静态变量也是各个线程共享的,所以,可以存在这里;

4.本地方法栈:native 修饰的方法 底层是C 语言或者C++实现的;它和栈的功能是差不多的,只是,栈是执行java方法,而本地方法栈是执行native方法服务。 早起跨语言交互就是使用这个本地方法栈来实现的;

5.堆:新生代 + 老年代:默认情况下 年轻代占 1/3 老年代占2/3;
新生代:有Eden From To
Survivor :有 From To
在这里插入图片描述
这里面有调优相关的知识点;
1.如果,Eden中,内存满了怎么办?这时候,就会调用GC垃圾回收(minor gc);比如说:GC Roots
它会通过方法区中的对象,以及栈中选择所有的对象,然后,来进行查找,如果,堆中的对象,可以通过方法区或栈中对象查找到,那么,就说明它不是垃圾对象,直接就复制到Survivor区(有复制算法的);Eden区中,剩下的就直接进行清理;如果,经过一次GC Roots还没有进行清除,这时候他的分代年龄就会+1(存在对象头中 现在学过的东西存在对象头里有:1.锁状态 2.分代年龄);
2.如果第二次,Eden中满,进行垃圾回收时,会对Eden 和 From 执行GC Roots算法,如果,对象还存在,就复制到To区;
3.如果第三次,Eden中满,进行垃圾回收时,会对Eden 和 To执行 GC Roots 算法,如果,对象还存在,就复制到From区;
执行 2 3 点的问题,是因为From 和 To 区也需要进行垃圾回收,这两个区中存的数据也有可能是垃圾对象;每一次 分代年龄会加1;
如果,分代年龄加到了15,就会给复制到了老年代;
如果,老年代满了,会执行full gc,如果回收不到内存,而且,还想加东西,就会内存溢出;
注意:在GC的时候,会把应用线程给停掉 stop work (stw);这体验是很不好的,这就是Java虚拟机调优的目的:减少stw,影响用户的体验; minor gc 它收集的还范围还少一点,full gc是收集整个堆空间,收集的范围比较大;所以,优先调节full gc;
jvm虚拟机调优例子:假设一个电商网站,一秒钟能产生300单每秒;然后,计算每个订单可能涉及到的对象,假设结果为每秒能产生60MB的对象。注意,这些对象,处理完就成了垃圾对象,假设1s后就成为垃圾对象;
在这里插入图片描述
新说一下背景:
1.按照经验分配,如果,线程每秒产生60MB对象,那么,在堆的eden区,大概是每13s触发一次minor gc;这时候,因为对象1s后就过期了,所以,会有60MB的对象到From 和 To区,由于,这两个区的内存大小为100MB,而且,当加入的内存超过这两个区的50%时,会直接加入到老年代;所以,这60MB会直接加入到老年代;所以,经过5-6分钟以后,老年代就会数据满了。就执行full gc;由于,full gc 扫描的范围比较广,花费的时间比较久;但是,一扫描 发现大部分的数据都是垃圾对象;基本上,会清空为60MB;
那么,对于,这种情况,虚拟机调优应该怎么调呢?由于,大部分对象很快就会销毁,所以,尽量不要让他们到老年代, 调优:把老年代的内存改小,把新生代的内存改大;
在这里插入图片描述


垃圾收集器考虑的问题:1.哪些内存需要回收;2.什么时候回收;3.如何回收;
针对Java内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的。因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要如何考虑回收问题,当方法结束或者线程结束时,内存自然就跟随者回收了。
Java堆和方法区这两个则有着很显著的不确定性:只有处于运行状态,才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收时动态的,垃圾收集器所关注的正是这部分内存该如何管理;
垃圾收集器
如何判断对象已死? 1.引用计数算法 2.可达性分析算法
java, C#选用的是可达性分析算法来判断对象是否存活的。
可达性分析算法 基本思路:通过一系列称为“GC Roots”的根对象作为起始结点集,从这些结点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,则GC Roots到这个对象不可达,证明该对象是不可能再被使用的。
在Java体系中,固定可作为GC Roots的对象包括以下几种:(从方法区和栈中的对象开始查找去)
1.在栈(栈帧中的本地变量表)中引用的对象,如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量;
2.在方法区中类静态变量属性引用的对象,
3.在本地方法栈中引用的对象
4.在方法区中常量引用的对象,字符串常量池里的引用。
5.所有被同步锁持有的对象
除了:这些固定的GC Roots 集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,会共同构建完整的GC Roots集合,譬如后文将会提到的分代收集和局部回收;P71
再谈引用
在1.2版本之前,如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称为该reference数据代表的是某块内存,某个对象的引用。
但是,这样功能还是不够的,比如说:我们需要描述这一类对象:当内存空间还足够时,能保留在内存中,如果内存空间在进行垃圾回收之后仍然非常紧张,那就可以抛弃这些对象–很多系统的缓存功能都符合这样的应用场景;
在jdk1.2版本之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、和虚引用,这四种引用的强度依次逐渐减弱;
1.强引用:最传统的应用,只要强引用还存在,垃圾回收器就永远不会回收掉被引用的对象;
2.软引用:指代有一些用,但非必须的对象,在系统要发生内存溢出异常之前,会把这些对象列入回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
3.弱引用:被弱引用关联的对象只能生存到下一次垃圾收集发生为止;
4.虚引用:比弱引用还弱,存在的唯一目的是在这个对象被垃圾收集器回收时,收到一个系统通知;

常量池
字符串常量池 在堆中 ;
常量池在方法区中;常量池保存字面量和符号引用;
符号引用包括:1.类的全限定名 2.字段名和属性 3.方法名和属性;
分代收集理论
详细信息:p75
根据,分代收集理论,收集器应该将Java堆划分出不同的区域,然后将回收对象依据其年龄分配到不同的区域之中进行存储,垃圾收集器每次只回收其中某一个或者某些部分的区域,所以,才有了Young GC、 Old GC、 Full GC;但是,这会产生一个问题,对象不是孤立的,对象之间会存在跨代引用,假如要进行一次只局限于新生代区域内的收集,但新生代中对象完全可能被老年代所引用,为了找到该区域的存活对象,不得不额外遍历整个老年代中所有对象来确保可达性分析结果的正确性;效率大大下降;为了解决这个问题,在新生代上建立一个全局的数据结构(记忆集)这个结构把老年代划分为若干小块,标识出老年代的哪一块内存会存在跨代引用,当发生Young GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描;
清除算法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值