狂神说 JVM入门

JVM:虚拟机 java的运行环境

JVM运行在操作系统上

 

1.终端里执行javac命令把.java文件变成.class文件

2.把class类通过类加载器(Class Loader)加载到JVM虚拟机里才能运行。

JVM内存图

线程共享区:堆,方法区;随着虚拟机启动/关闭而创建/销毁

线程私有区:生命周期与线程相同,随着线程的启动/关闭而创建/销毁

JVM调优:基本都是指堆

类加载器

作用:把磁盘里的.class文件加载到JVM内存中

打印类加载器

打印其父类加载器

appClassLoader>>>extClassLoader>>>bootStapClassLoader

为什么是null?因为那是这个最父的类加载器是c写的,java调不到

bootStapClassLoader:D:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar

extClassLoader:D:\Program Files\Java\jdk1.8.0_111\jre\lib\ext

appClassLoader:D:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar\java.lang.ClassLoader

类加载器的双亲委派机制:

1.首先类加载器收到类加载请求

2.将这个请求向上委托给父类加载器,一直向上委托,直到启动类加载器

3.启动类加载器是否能够加载这个类,能加载就结束使用当前类加载器否则抛出异常,通知自类加载器进行加载

问题:如果我自己定义一个类叫String并且还创建了一个java.lang文件夹装它怎么办

运行时会抛出异常ClassNotFound

问题:如果我把我自己写的类编译的class放在其父类加载器的文件夹里能运行吗

能,但是!最后别动它,覆盖了以前叫这个名字的类就没了

沙箱安全机制

字节码校验器:即编译器

类加载器

类文件校验器

类管理器

native关键字:没听懂咋就能和别的语言交互通信呢?

方法区

方法区里都有什么:静态变量,常量,类信息,运行时的常量池(static,final,class模板...new的对象虽然在堆中,但是new一个类都会在常量池中有一个模板)

以上代码的内存分配图

先进后出

和栈对应的是队列(先进先出)FIFO

示例代码内存图

 

生命周期:和线程同步

线程结束:栈内存释放了,所以对栈来说,不存在垃圾回收! 

栈运行原理,栈帧 每个方法执行的时候都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等

方法调用对应方法入栈对应栈帧创建

执行完成对应方法出栈对应栈帧销毁

本地方法栈和虚拟机栈的区别

本地方法栈:Native方法服务(别的语言封装的方法库啥的)

虚拟机栈:执行java方法服务

栈满了,StackOverError

堆(Heap)

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

JVM调优通过这里来传参

 堆中内存分三个区:新生区(Young/New),养老区(Old),永久区(Perm)

GC垃圾回收主要发生在Eden区

堆内存满了:OOM(OutOfMemoryError)

从GC回收的角度分为

新生区: 类生长或死亡的地方

Eden:new出来的,满了触发轻GC(GC)

幸存者区:分为from区和to区,都满了触发重GC(Full GC)

养老区:剩下的进入养老区

永久区:这个区域常驻内存,用来存放JDK自身class对象,存储JAVA运行时的环境或类信息,不存在垃圾回收,关闭JVM才会释放这个区域的内存

JDK1.6之前叫永久代,常量池在方法区里

JDK1.7还在堆中

但是在JDK1.8以后没有永久代,取而代之的是元空间(元空间只是逻辑上在堆里物理上不存在

Tomcat部署了太多应用,大量动态生成反射类,不断地被加载,直到内存满,出现OOM

(但是其实这个图还是不对,JDK1.8叫元空间,不在虚拟机中而是使用本地内存)

默认情况下,分配的总内存是是电脑内存的1/4,初始化内存/总内存=1/4

打印堆内存情况 -XX:+PrintGCDetails 

调参语句,把JVM初始化大小内存和JVM最大内存设置成一样:-Xms1024m -Xmx1024m 

(截取自GC调优-XX:PrintGCDetails深度解析 - debug的勇士 - 博客园

面试问题:万一遇到OOM

1.首先把堆内存空间变大

2.要是扩大了还提示OOM,检查代码有没有死循环代码

3.分析内存看哪里有问题

使用JPofiler工具分析OOM原因(9)

GC垃圾回收(10)

分代收集算法(这是我画的略有捡漏)

 什么是复制算法?

创建(new)出来的对象存放在Eden区和From区中,当整两个区的内存到达一定的占用量后,会进行轻量级的垃圾回收(Minor GC),将存活下来的对象年龄+1,并将存活下来的对象复制到To区,此时From区和To区进行交换(区分From区和To区:谁空谁是To,并且From区和To区是不断交换的)。当一个对象的年龄达到15时,将此对象从新生代移动到老年代。优点:不产生内存碎片问题,能保持对象的完整性。缺点:因为要一致保持To区是空的,浪费了一定的内存空间。
(https://blog.csdn.net/AD_plus/article/details/99448182)

新生代主要用到复制算法,因为新生代对象存活率较低

好处:没有内存碎片

坏处:多了一半to区,永远是空的,很浪费空间

(摘抄自  Java虚拟机详解04----GC算法和种类【重要】 - 千古壹号 - 博客园)

什么是Mark-Sweep?
标记清除算法:Mark-Sweep

步骤一:标记:从根集合开始扫描,对存活的对象进行标记;

步骤二:清除:再次扫描整个内存空间,回收未被标记的对象,使用free-list记录可用区域。

缺点:两次扫描,耗时严重,会产生内存碎片。(内存碎片就是指很小的内存空间,不够给别人分配的,就只能浪费掉了)

优点:不需要占用额外空间。

什么是Mark-Compact?
标记整理算法:Mark-Compact标记/整理算法

引入:

    如果在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选中这种算法。

概念:

标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记;但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间。

cc79889a-0856-4018-92c3-c51108c9caea

  • 标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。
  • 整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

上图中可以看到,标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。

标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。

  • 但是,标记/整理算法唯一的缺点就是效率也不高,因为多了移动成本。

不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,标记/整理算法要低于复制算法。

所以一般应该先sweep几次,再compact,JVM调优就调这个,搜集了多少内存碎片之后再压缩!
复制算法>标记清除算法>标记整理算法。
内存整齐度:复制算法=标记整理算法>标记清理算法。
内存利用率:标记整理算法=复制算法>标记清理算法。

补充概念(吞吐量:用户程序时间/用户程序时间+GC时间)

JAVA内存模型JMM(Java Memory Model)

作用:MESI缓存一致性协议,用于定义数据读写规则

java多线程:容易出现读写异常

volatile原子性解决了共享对象可见性

synchronized同步性

java的主内存只有一个,每一个线程是copy出一个自己的工作台

JMM定义了线程和主内存的抽象关系

线程之间的共享变量存在主内存中,每个线程都有一个私有的本地内存

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值