JVM详细解析

一、java代码的编译及执行原理

将Java代码通过编译器编译成.class文件,是一个二进制字节码文件,然后再通过解释器,解释成机器码,最后在CPU中进行执行

JVM各组件介绍

CPU(寄存器):记住jvm中下一条指令的执行地址。CPU中的寄存器来当作程序计数器。
特点:
1.是线程私有的
2.不会存在内存溢出
:线程运行时,需要的一个内存空间,由一个个栈帧组成,一个栈帧就对应着一个方法的调用,栈帧中主要存储着方法参数,局部变量,返回地址等信息。栈只对于线程是可见的。所以是线程私有,他的生命周期和线程相同。
-Xss 设置大小
思考:
1.栈内存需要垃圾回收进行回收吗?
答:不需要,因为每次方法调用执行完毕后都会弹出栈,自动就释放了

2.栈空间是否越大越好?
答:不是,栈内存的增加会导致执行线程数的减少,因为物理内存的大小是固定的。栈内存的增加也只是能够进行更多次的方法的递归调用

3.方法内的局部变量是否是线程安全的?
答:1.如果局部变量没有逃离方法的作用范围,则是线程安全的
2.如果该局部变量引用了对象,并且逃离了作用范围,则是线程不安全的.

4.什么情况下会导致栈内存溢出?
答:栈帧过多,导致栈内存溢出,比如递归调用,未设置结束条件
栈帧过大,导致占内存溢出,比较少见
第三方库的使用也可能导致,比如fastjson,当两个对象循环引用的时候,调用转json方法就会出现内存溢出问题

补充:虚拟机上定位占用内存过高的线程方法
案例1:CPU占用过多
1.使用 top 命令,查看并找到占用cpu最高的PID
在这里插入图片描述
2.使用命令ps找到具体的线程ID
ps H -eo pid,tid,%cpu | grep 32655
在这里插入图片描述
3.使用jstack命令,跟着进程ID 就可以列出所有的线程
jstack 32655
然后将找到的线程ID32665转换成十六进制,是7F99,然后在打印的线程中去找
在这里插入图片描述
找到后可以看到,具体的哪一行代码出的问题
本地方法栈:不是由java代码编写的方法,因为java代码是有一定的限制的,它不能直接的跟操作系统的底层打交道,所以需要一些用C或C++的方法调用到底层的一些功能

:它是线程共享的,堆中对象都需要考虑线程安全问题,通过new关键字创建的对象都会使用堆内存,有垃圾回收机制。堆内存可细分为一个Edon区和Survivor区,Survivor区分为from区和to区,新创建的对象都在Edon区,不同区的GC机制也不相同。
-Xmx设置堆空间大小
堆内存溢出:OutOfMemoryError
堆内存诊断
1.jps工具
可以查看当前系统中有哪些java进程
2.jmap工具
查看堆内存占用情况
3.jconsole工具
图形界面的,多功能的监测工具,可以连续监测
在这里插入图片描述

方法区:它也是线程共享的,主要存储 类的加载信息,常量池,运行时常量池。在JDK 7以前,习惯上把方法区称为永久代(习惯上),而到了JDK8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Metaspace)来代替。这两个最大的区别就是:元空间不在虚拟机设置的内存中,而是使用本地内存。
常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量等信息
运行时常量池:常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

垃圾回收机制

1.判断对象是否可回收

1.引用计数法:

当一个对象被其他对象引用时,计数+1,当不再引用时,计数-1,当计数为0时,该对象就会被回收掉。但是有个问题,当两个对象互相持有对方的引用时,也就是循环引用的时候,这两个对象都不会被回收,即使他们不再使用,这就导致了内存泄漏的问题

2.可达性分析:

首先要确定一系列根对象(GC root对象),根对象是肯定不能被回收的一些对象,在垃圾回收之前,首先会对堆内存中的所有对象进行一遍扫描,然后看每一个对象是否被根对象直接或间接引用,如果是,那么这些对象就不能被回收。

根对象:核心的系统类比如Object类,hashMap类等等,或一些native类,一些活动线程类,或一些正在加锁的对象都可以做根对象

2.引用类型有哪些,与回收有什么关系

强引用:就是我们常用的那些,比如new出来的对象都是强引用对象,垃圾回收时只有该对象不被其他对象直接或间接引用时才会被回收掉。
软引用:软引用在java中有个专门的SoftReference类型,软引用的意思是只有在内存不足的情况下,被引用的对象才会被回收。
弱引用:weakReference和softReference很类似,不同的是weekReference引用的对象只要垃圾回收执行,就会被回收,而不管是否内存不足。
虚引用:PhantomReference的作用是跟踪垃圾回收器收集对象的活动,在GC的过程中,如果发现有PhantomReference,GC则会将引用放到ReferenceQueue中,由程序员自己处理,当程序员调用ReferenceQueue.pull()方法,将引用出ReferenceQueue移除之后,Reference对象会变成Inactive状态,意味着被引用的对象可以被回收了。

3.垃圾回收算法

1.标记清除算法:首先通过可达性分析,将没有被根对象所引用的对象标记出来,然后进行清除。但是会产生内存碎片,一些大的对象会需要一部分连续的存储空间,这样就会导致内存的利用率降低
特点:速度快,但是会产生内存碎片
2.标记整理算法:与标记清除的不同点就在于,会将当前内存使用进行整理,使其更加紧凑,使得回收后也不会有这种内存碎片的情况
特点:速度慢,但是不会产生内存碎片
3.复制算法:有两个一样大小的内存区域,From区和To区,将还在使用的对象复制到To区内存中,然后清空From区的内存空间,然后再交换From区和To区的内容。
特点:它需要占用双倍的内存空间

虚拟机的垃圾回收

实际的垃圾回收不会是采用某一种回收算法,而是针对于不同内存区使用更合适的垃圾回收算法
堆内存大的可以划分为一个新生代,一个老年代,新生代又细分为三个区,一个Edon区,两个survie区,From区和To区。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。新创建的都对象都是在新生代中,再具体点就是在edon区,当edon区装满了时,就会出发一次Minor GC,先采用可达性分析对堆中对象做一次标记,然后采用复制算法,将edon区和from区存活的对象复制到To区,并且设置年龄为1,每经历一次垃圾回收年龄就会+1。然后再将From区和To区进行交换,当From区中某个对象的年龄达到15时,就会被晋升到老年代中。
minor GC时会引发一次 stop the world,在GC未完成时其他线程都会被暂停,只有GC完成后其他线程才继续执行
随着Minor GC的持续进行,老年代中对象也会持续增长,导致老年代的空间也会不够用,最终会执行Major GC(MajorGC 的速度比 Minor GC 慢很多很多,据说10倍左右)。Major GC使用的算法是:标记清除(回收)算法或者标记压缩算法。当新生代和老年代都满了放不下的时候,就会触发一次full GC

相关VM参数

堆初始大小 -Xms
堆最大大小 -Xmx
新生代大小 -Xmn
-XX:~ 幸存区比例,晋升阈值,晋升详情等


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JavaSupeMan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值