热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入理解jvm虚拟机(笔记)

jvm内存模型:1:方法区:存储常量池,静态变量,类型信息2:堆区:存放对象实力&

jvm内存模型:

 

1:方法区:存储常量池,静态变量,类型信息

 

2:堆区:存放对象实力,句柄(包括类的成员变量的引用与实力)

 

3:虚拟机栈:每个线程都有独有的虚拟机栈,执行每个方法都会创建栈帧,执行方法压栈榨,执行完毕出栈,存放局部变量,方法参数,返回信息,对象引用。

 

4:本地方法栈:与虚拟机栈类似,存储c,c++相关的信息。

 

5:程序计数器:存放线程相关的字节码指令地址。

 

 

方法区与堆区 是线程共享的, 虚拟机栈,本地方法栈,程序计数器是线程独享的。

 

 

锁的类型:

1:偏向锁:线程独占的锁,默认不会存在其他线程与之争抢。

2:轻量级锁:锁竞争不激烈的情况。

3:重量级锁:锁竞争激烈。

自旋锁可对轻量级锁优化,短时间内高概率抢夺锁成功前提下,自旋锁使用有限循环来代替线程阻塞从而降低切换线程引发的开销,单核cpu下无意义。

 

 

内存溢出:

哪些内存区域会引发内存溢出?

1,堆区内存溢出:-Xms 8M 最小分配内存 -Xmx 20M 最大分配内存 -XX:+HeapDumpOnOutofMemoryError 可以在发生内存溢出是Dump出当前内存快照以便分析,触发场景:在循环内不停创建对象。

 

2,程序计数器不会引发内存溢出。

 

3,虚拟机栈和本地方法栈溢出:-Xss 设置虚拟机栈大小  -Xoss 设置本地方法栈大小  HotSpot虚拟机不区分虚拟机栈与本地方法栈,-Xoss实际无效

触发场景:递归调用方法,让虚拟机债深度不停增大,这个时候会引发StackOverFlowError 栈溢出,而要触发内存溢出,需要不停创建线程,耗尽虚拟机栈的内存极限。

 

4,方法区和运行时常量池溢出:-XX:PermSize —XX:MaxPermSize 限制方法区大小,间接限制常量池大小

触发场景:String.intern() 如果常量池的常量不存在,会在常量池中创建常量,循环调用这个方法创建不同常量,最后会引发方法区OutOfMemory,

同时方法区中类的类型信息也会造成溢出,例如使用cglib,动态代理等动态生成字节码的框架的情况,循环创建动态对象造成溢出。

 

 

5,本地直接内存溢出:-XX:MaxDirectMemorySize 通过这个指定,如果不指定,默认和堆一样。

触发场景:可以使用java提供的Unsafe,不过需要通过反射获取,然后调用allocateMemory方法,不停分配内存,造成内存溢出。这种溢出在HeapDump文件中是看不到的。

 

 

垃圾回收与内存分配策略

c,c++使用引用计数器来判断内存是否可以回收,而java中由于对象相互引用的存在,这种方式就变得不可行,所以引入了GC root方式,垃圾回收期判断对象是否可达来进行回收操作,一个对象到GC Root没有任何引用链,则就可以被回收。

能成为GC Root的 :成员变量引用的对象,方法区中静态与常量引用的对象,本地方法栈中JNI引用的对象。

引用类型:强引用:永远无法被回收,软引用:没有足够内存,引发内存溢出前被回收,弱引用:随时可能被回收,虚引用:无法通过虚引用来获得实例,只是在实例被回收的时候会发一个通知,其实是用来监测对象被回收的。

 

垃圾回收算法过程:

当一个对象需要被回收时,会被经过一次标记,如果对象没有复写finalize()方法,或者已经执行过finalize()方法,则被清除。

finalize()是对象起死回生的一个关键点,但是没什么卵用。

java堆区内存被划分为年轻代与老年代,年轻代又分为eden 和 两个小的survivor区 默认8:1:1

1,标记-清除算法:这种算法会把不可达对象作标记,然后清除对象,这样会造成内存碎片化。

2,复制算法:年轻代中eden和其中一个survivor区域被使用,垃圾回收期把还存活的对象复制进入另一个survivor区域中,然后把eden和另一个survivor区域清除,这种算法在还有大量对象存货的情况下比较耗资源,同时如果存活的对象比较多,那survivor空间不足,放不下的对象会存放进老年代中。

3,标记-整理算法:这种算法是将存货对象指针移动整理至内存开始区域,然后清除区域外的内存空间,适合老年代内存。

4,分代收集算法:一般只有少量对象存活就使用复制算法,老年代中对象存活率高,没有额外空间担保,所以采用标记-清楚,标记-整理算法。

 

安全点:

虚拟机需要记录GC root,一般而言类加载完成就会得到一个oopMap,这个oopMap存放着GC root,但是在运行时必须要找到安全点 才能动态去查询GCroot,一般安全点会在方法调用,循环跳转,异常跳转的时候。

对于多线程,有两种方式,抢占式中断和主动式中断,抢占式中断是中断所有线程,发现其中线程没有进入安全点的时候就恢复线程让其跑到安全点上, 不过几乎没有虚拟机采用这种方式,一般会采用主动式中断。GC在需要中断线程的时候,会设立一个标志,每个线程主动轮询这个标志,发现中断标志就中断挂起,轮询标志的地方和安全点是重合的。

设立安全点看似很完美,但还有情况是当程序不执行,线程休眠的时候(cpu没有分配时间片),这种情况就需要安全区域来解决。

安全区域是指线程执行到区域中的任何地方开始GC都是安全的,线程进入安全区域的时候,首先标识自己已经进入了安全区域,当这个时间里jvm发起gc时候就不需要管进入安全区域的线程了。线程要离开安全区域时,要检查系统是否已经完成了gcroot 枚举,如果完成则继续执行,否则等待直到收到可以安全离开安全区域的信号为止。

 

垃圾回收器:

 

serial收集器:这种收集器是单线程收集器,进行垃圾收集的时候会中断工作线程。

 

parallel收集器:相对serial收集器来说,是多线程的实现。

 

cms(concurrent Mark Sweep)收集器:老年代并行收集器,可以让收集与用户线程同时工作,重视GC时间短,适合服务器,响应速度快,停顿时间短。

 

parallel Scavenge收集器:吞吐量优先收集器。如果设置吞吐小,那gc频繁。可以自适应调节的收集器。

 

serial old 收集器:是serial收集器的老年代版本,使用标记-整理算法,是cms收集器的后备方案,与parallel scavenge收集器配合使用。

 

parallel Old 收集器:是parallel scavenge收集器的老年代版本,使用多线程和标记-整理算法,也是注重吞吐量。

 

g1收集器:并行并发,分代收集,空间整合,可预测的停顿,将java堆划分成多个大小相等的独立区域,还保留新生代和老年代概念,但是不存在物理上的隔离, g1中新生代,老年代都是独立的区域。

g1收集分为四个部分:初始标记—主要标记与GCroot直接链接的对象, 并发标记—从gcroot开始对堆中对象进行可达分析,最终标记:修正用户线程产生的变动,需要中断线程,筛选回收—对区域中回收价值与成本排序,根据用户期望的GC停顿时间制定回收计划。

 

分析jvm虚拟机性能工具:

 

jps 查看java所有进程以及进程id   -v显示虚拟机启动时的参数  -m显示传递给主类main()的参数  -l显示主类全名 -q只显示进程id

 

jstat 查询虚拟机进程的类装载,内存,垃圾收集,jit编译等运行数据, ( jstat -gc 进程号 250 20   分析gc堆状况), 文本显示,不够直观

 

jinfo 查看虚拟机各项参数,可对可调整的参数作出调整

 

jmap 对java堆内存转储成快照.

 

jhat 对java堆快照的分析工具,是一个微型的html服务,所以不太常用

 

jstack java堆栈的跟踪工具,生成线程快照,是一个顺时的状态数据,在java中可通过Thread的 getAllStackTraces获取所有线程的堆栈信息(主要用来分析线程长时间停留的原因)

 

hsdis是虚拟机jit编译代码的反汇编插件. 该插件可以让虚拟机通过jit即时编译生成的本地机器码反汇编成汇编代码来分析程序的执行逻辑。

 

jconsole,visualVm jdk中监控和分析的可视化工具. jconsole是jdk1.5时出现的,visualVm 是jdk1.6时出现的。

 

 

字节码详解:

每个字节码文件都是由8位字节构成,其中前4个字节是字节码文件的唯一标识,也称为魔数,固定是0xcofebabe 5,6两个字节代表小版本,7,8两个字节代表大版本,第九个字节代表常量池的容量。容量池大小从1开始计算,0号位预留来代表不引用任何常量的含义。

 

常量一般从字面理解,分为字面量和符号引用,包含字符串常量和final修饰的常量值等,而从编译原理区分,分为三类:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。

 

虚拟机类加载机制:双亲委派机制,实现方式不是通过集成,而是通过组合。

 

bootstrap classloader ——>extension classloader —> application classloader—>user classloader 

 

bootstrap classloader 加载  JAVA_HOME/lib 目录下的类库

 

extension classloader 加载 JAVA_HOME/lib/ext 目录下的类库

 

user classloader 加载 用户classpath下的类库

 

user classloader 加载类时会试图从父加载(application classloader)加载,父加载器加载不到再从extension class loader加载 ,extension classloader加载不到从bootstrap classloader加载  如果还加载不到才会使用user classloader去加载。


推荐阅读
  • jvm内存区域与溢出为什么学习jvm木板原理,最短的一块板决定一个水的深度,当一个系统垃圾收集成为瓶颈的时候,那么就需要你对jvm的了解掌握。当一个系统出现内存溢出,内存泄露的时候 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了GregorianCalendar类的基本信息,包括它是Calendar的子类,提供了世界上大多数国家使用的标准日历系统。默认情况下,它对应格里高利日历创立时的日期,但可以通过调用setGregorianChange()方法来更改起始日期。同时,文中还提到了GregorianCalendar类为每个日历字段使用的默认值。 ... [详细]
  • Java源代码安全审计(二):使用Fortify-sca工具进行maven项目安全审计
    本文介绍了使用Fortify-sca工具对maven项目进行安全审计的过程。作者通过对Fortify的研究和实践,记录了解决问题的学习过程。文章详细介绍了maven项目的处理流程,包括clean、build、Analyze和Report。在安装mvn后,作者遇到了一些错误,并通过Google和Stack Overflow等资源找到了解决方法。作者分享了将一段代码添加到pom.xml中的经验,并成功进行了mvn install。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • 本文整理了Java中org.gwtbootstrap3.client.ui.Icon.addDomHandler()方法的一些代码示例,展示了Icon.ad ... [详细]
  • AndroidStudio 2.3迁移3.0踩坑之——Could not resolve project
    参见StackOverflow如果你的项目引用了自己的库,在迁移到3.0后,编译就会报错。Error:Failedtoresolve:Couldnotresolveproject: ... [详细]
author-avatar
智慧曜彰_272
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有