JVM的一点总结

java程序跑在jvm上面,jre-jvm,在操作系统之上,最底下是硬件系统。

一、JVM的体系架构图

jvm调优说的是“方法区”和“堆“,而”栈“、”本地方法栈“、”程序计数器“不存在垃圾回收一事。

虚拟机试图使用最大内存为电脑内存的1/4, 而jvm初始化内存为1/64(-Xms1024m -Xmx1024m -XX:+PrintGCDetails)

二、类加载器

(1) 在什么时候才会启动类加载器?

类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。

(2) 从哪个地方去加载.class文件

在这里进行一个简单的分类。例举了5个来源

(1)本地磁盘

(2)网上加载.class文件(Applet)

(3)从数据库中

(4)压缩文件中(ZAR,jar等)

(5)从其他文件生成的(JSP应用)

定义: 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构

作用:加载类文件,引用在栈中,具体实例在堆中

虚拟器自带三种类加载器启动类(根)加载器扩展类加载器应用程序加载器(系统类加载器)

(3) 类加载过程

其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

 三、双亲委派机制

当某个类加载器需要加载某个`.class`文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

四、沙箱安全机制 Java安全模型核心   

沙箱:限制程序的运行时环境   

域Domain概念   

将java代码限定在虚拟机特定的运行范围内,严格限制代码对本地系统资源的访问,这样措施保证对代码的有效隔离,防止对本地系统的破坏

五、Native

调用底层c,c++语言库   - native -> JNI -> 本地方法接口 -> 本地方法库   

本地方法栈 , 一般用的不多,硬件开发用的多

native:凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层C语言的库

会进入本地方法栈

调用本地方法本地接口JNI

JNI作用:扩展java的使用,融合不同的语言为java所用!最初:C、C++

在最终执行的时候,通过JNI加载本地方法库中的方法

六、pc寄存器

程序计数器Program Counter Register

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

七、方法区(Method Area)

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间。

静态变量,常量,类信息(构造方法,接口定义)运行时的常量池也存在方法区中,但是实例变量存在堆内存中,和方法区无关

八、栈(stack)

栈:先进后出,后进先出,堆:先进先出,后进后出

栈内存,主管程序的运行,生命周期和线程同步

线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题

一旦线程结束,栈内存释放

程序正在执行的,一定在栈顶部

栈:8大基本类型+对象引用+实例的方法

栈帧:父帧子帧,每一个在执行的方法都会产生栈帧

栈满报错:StackOverflowError

对象实例化示意图:

九、堆(Heap)

一个JVM只有一个堆内存,堆内存的大小是可以调节的。

类加载器读取了类文件后,一般会把类,方法,常量,变量等放到堆中,保存我们所有引用类型的真实对象

3个区域

新生区(伊甸园): 所有的对象都new在了伊甸园去,从中出生

养老区-- 老年区 --从新生区(from to )中顺下来的,干不掉,杀不死,幸存区,哪个空,哪个就是to

永久区:此区域不存在垃圾回收,但是也会崩( 启动了大量第三方jar) --

永久区是一个常驻内存区域,用于存放JDK自身携带的Class Interface的元数据,存储的是java运行时的一些环境或类信息,这个区域不存在垃圾回收!

一个启动类,加载了大量的第三方jar包,Tomcat部署了太多的应用,大量动态生成的反射类,永久区可能会崩掉

幸存区不会出现满了溢出,GC垃圾回收,主要是在伊甸园区和养老区

堆内存满了,会报OOM(java.lang.OutOfMemoryError: Java heap space)

dk1.6之前永久代,常量池是在方法区

jdk1.7永久代,慢慢退化,去永久代,常量池在堆中

jdk1.8无永久代,常量池在元空间

关闭虚拟机就会释放这块内存

gc垃圾回收主要针对新生区(伊甸园区)和养老区

设置分配内存和初始化内存大小,并打印

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

-Xms 设置初始化内存分配大小 默认内存的1/64

-Xmx 设控制最大分配内存 默认是内存的1/4

-XX:+PrintGCDEtails 打印GC垃圾回收信息

-XX:+HeapDumpOnOutOfMemoryError 打印内存溢出信息

出现内存溢出处理方法:

(1)首先调大堆内存大小

(2)分析内存,看一下哪里出现问题,用专业的工具

能够看到代码第几行出现问题,内存快照分析工具Jprofiler

JProfiler作用:

分析Dump内存文件,快速定位内存泄漏

获得堆中的数据

获得大的对象

十、GC垃圾回收

JVM在进行GC时,并不是对这三个区域统一回收。大部分时候,回收的都是新生代

新生代

幸存区(from,to)

老年区

GC两种类:轻GC(普通的GC),重GC(全局GC)

题目:

JVM的内存模型和分区,详细到每个区放什么

堆里的分区有哪些?Eden、from、to,老年区,说说他们的特点

GC的算法有哪些?标记清除法、标记压缩、复制算法、引用计数器,说一下他们之间的特点,怎么用?

轻GC和重GC分别在什么时候发生?轻GC在伊甸园区满的时候触发,重GC在整个新生区满的时候触发。

(1)引用计数法

(2)复制算法

幸存区from和to是来回切换的,哪个空,哪个就是to

1)每次GC都会将Eden活的对象移到幸存区中,一旦Eden区被GC后,就会空的!

2)当一个对象经历了15次GC(默认),都还没有死,就会进入养老区,-XX:MaxTenuringThreshold=5,

可以通过MaxTenuringThreshold参数设定进入老年代的时间

绿色代表活着的数据,从Eden复制到幸存区To里面,同时幸存区From中的幸存者也复制到To里面,这时原幸存区From变为To,原To变为From,Eden变成空的了;若From中的数据有经历过15次GC,活着的就进入养老区,死掉的回收掉。 好处:没有内存碎片

坏处:浪费内存空间,多了一半空间永远是空to。假设对象100%存活(极端情况)

复制算法最佳使用场景:对象存活度较低的时候;新生区

(3)标记清除算法

扫描对象,对活着对象进行标记,然后对没有标记的对象,进行清除

 

优点:不需要额外的空间

缺点:两次扫描,严重浪费时间,会产生内存碎片

(4)标记压缩算法:在标记清除算法基础上,向一端移动存活对象,以防止碎片产生

总结

内存效率:复制算法>标记算法>标记压缩算法(时间复杂度)

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存利用率:标记压缩算法=标记清除算法>复制算法

根据不同区域选择最合适的算法,回收垃圾

年轻代:由于存活低,采用复制算法

老年代:由于区域大,存活率高,采用标记清除+标记压缩混合实现,在清除过程中,碎片过多时,可以压缩后,再继续清除。

补录:

GC

  • 垃圾回收为自动,手动只能提醒
  • GC作用于堆+方法区
  • GC大部分针对新生代
    • 轻GC ----- 普通GC
    • 重GC ----- 全局GC
  • GC算法
    • 复制算法 ---
    • GC算法-复制算法
    • 该算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去
      • 幸存区01, from...to..., 0和1互相不断交换,进行gc进行复制算法
      • 若一直没有死进入到养老区
      • 优点:实现简单,不产生内存碎片
      • 缺点:浪费一半的内存空间
  • 标记清除算法 -----扫描对象,对活着的对象进行标记, 对没有标记的对象进行清除
    • 优点:不需要额外空间,优于复制算法
    • 两次扫描,浪费时间,会存在内存碎片
  • 标记压缩算法 ----- 再优化,压缩:防止碎片的产生, 方法: 向一端移动活的对象,多了一个移动成本
  • 标记清除压缩算法 ----- 先标记清除几次再进行压缩,等碎片多了之后
  • 引用计数算法 ------ 每个对象一个计数器,一般不用,因为计数器有消耗,用过多次的不删,0次的就删除了 ---引用出现+1,引用删除-1
  • 总结:
    • 内存效率:时间复杂度:复制算法 > 标记清除 >标记压缩
    • 内存整齐度:复制算法=标记压缩>标记清除
    • 内存利用率 ----- 标记压缩=标记清除 > 复制算法
    • 分代收集算法(JVM调优): 没有最好的算法,只有最合适的
    • 年轻代 ----- 存活率低,复制算法
    • 老年代 ----- 存活率高, 标记清除与标记压缩混合实现

JMM:

1、什么是JMM(百度百科)

JMM:(Java Memory Model)java内存模型

2、它是干嘛的?官方文档、博客和相关视频

作用:缓存一致性协议,用于定义数据读写的规则(遵守,找到这个规则)。抽象概念, 理论

定义了线程工作内存和主内存之间的抽象关系

线程之间的共享变量存储在主内存中 ----MAIN MEMORY

每个线程都有一个私有的本地内存 ---- LOCAL MEMORY

与并发相关

OOM的种类和原因

java.lang.OutOfMemoryError

 

 

为什么java需要采用分代回收思想?

防止对象全部进入老年代,撑爆内存,采用分代回收,就是尽量在新生代就把对象回收掉,让少量的对象进入到老年代,减少STW次数,提高性能。

https://www.cnblogs.com/yang-lq/p/8651494.html

在生成.class文件所在目录使用命令javap,可以生成jvm可以识别的指令,查看计算机java的执行情况

javap -c hello.class

javap -c hello.class > app.txt //将编译成的指令写到app.txt文档中

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值