java jvm总结(全)

class文件

魔数:标识class文件

java编译版本

常量池:类名方法名,变量名等等这些字面量,final修饰的基本数据类型,字符串

类访问标识

类索引,父类索引:标识出类名,父类名(在常量池中)

字段表:说明字段信息

方法表:说明方信息

属性表:说明上面的详细信息

类加载过程:

加载--连接--初始化--使用--销毁

加载:jvm没有固定发生时间一般是懒加载,使用的时候加载

加载数据生成class对象

连接:与加载并行

验证:验证class文件格式

准备:将字段的值设置为初始值(0,null),或者常量池中的值(final)

解析:将常量池中的引用变成直接引用;例如常量池中的类名和接口名

初始化:jvm规定了严格的执行时间

new该类对象的时候

访问一个类的静态字段

调用该类的静态方法

使用反射调用该类时

初始化该类必须先初始化该父类

main方法所在类

 

下面情况不会被初始化

通过子类调用父类静态字段,该子类不会被初始化

调用该类常量

使用数组定义对象

初始化都做了什么事:执行clinit方法

clinit方法=类变量的赋值动作+静态语句块

执行顺序按照定义的顺序来的;静态语句块只能访问定义在它之前的类变量;在他之后的只能赋值不能访问

子类的clinit方法执行之前,虚拟机会保证他父类的clinit方法先执行

接口中有变量需要赋值也会生成clinit方法执行,但是不会先执行父类的clinit,只有当父类的变量被访问时才初始化

对象的创建过程

new类名-->根据类名在常量池中查找类的符号引用-->么有找到就类加载-->堆中分配内存-->初始化内存零值-->调用构造方法

类加载器

做的事情:通过类的全限定名获取该类二进制流

被不同加载器加载的相同同一份字节码生成的类也不同

启动类加载器:加载javahome下的lib目录下的类

扩展类加载器:加载javahome下的lib/ext目录下的类

应用程序类加载器:加载构建路径(classpath)上的类

自定义类加载器:自己定义

集成classloader类

重写loadclass方法

双亲委派模型

当类加载器收到加载请求之后,把加载任务给父类加载器去加载,每层如此

父类加载器加载不了的话(抛异常或者加载后class仍为null)在交给子类,最低到第一

个发起的,不能再往下了

优点:基础的类被上层的类加载器加载,保证基础类稳定可用,不会出现混乱

举个栗子:你自己定义一个lang.object类运行时他将不会被加载,因为会传递给启

动类加载器,或者启动类加载起已经加载了

双亲委派模型并不是强制性的,但是自定义类加载器的时候java建议这样做

数据运行时区域

方法区(线程共享):加载后的类信息,常量等信息,常量池

运行时常量池和常量池:

常量池类中定义的字面量,符号引用存储的地方

运行时常量池:intern方法可以把对象放到常量池中

程序中" "定义的都在常量池中,a=new String(" ")都在堆中;a.intern()就把它也放入常

量池了返回常量池中地址;" "+" "也会放入常量池,生成新的常量,常量池是不会出现重

复值得变量引用的+是不会放入常量池的,相当于new新的

1.8修改:方法区不在jvm里面了,改成元数据区使用本地内存,jvm会分配和释放本地内存;不收jvm内存限制了,参数-XX:PermSize 和 -XX:MaxPermSize 参数替换为 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize

常量池和静态变量从方法区剥离出来,放入堆中

堆区(线程共享):new出来的对象

-xms:初始堆大小,默认物理内存的1/64

-xmx:最大堆大小,默认物理内存的1/4

虚拟机栈(线程独占):表示每个方法执行的内存模型;每个方法的执行都会创建一个栈帧;方法的开始和结束会使栈帧入站和出栈

-Xss:堆栈大小,默认1m

栈帧结构:

局部变量表:基本数据类型值,和栈中对象的引用

方法返回地址等信息

本地方法栈(线程独占):和虚拟机栈类似,用于执行本地方法

程序计数器(线程独占):记录正在执行的虚拟机指令的地址

 

直接内存:nio使用的bytebuffer引用的一段内存,不在jvm管理之内,也会出现

outofmemory异常,受制于电脑内存

对象内存分配规则:

优先在eden区域分配

大对象直接进入老年代

长期存活的对象进入老年代

空间分配担保:新生代gc时survival内存不够了,放入老年代

垃圾回收器的算法,选择

判断对象是否存活:引用计数法,可达性分析法

默认:可达性分析法

可作为 GC Root 的对象有:

1.Java虚拟机栈(栈帧的本地变量表)中引用的对象

2.本地方法栈 中 JNI引用对象

3.方法区 中常量、类静态属性引用的对象

垃圾回收算法:

标记清除:标记可回收对象直接收掉

复制:把内存分成几个区域,使用一部分区域,回收时把存活对象复制到空闲区域,清空使用了的区域(Eden,2*survive);适合新生代

先放eden和一个survive1,回收把活下来的放入survive2,清除eden和一个survive1,然后放入eden和survive2,循环。。

标记整理:比标记清除算法多一个步骤;清除完之后把存活对象移向一端,适合老年代(cms采用标记清楚)

分代收集:根据对象的生命周期长短把内存分成几个区域(一般都是,新老分开收)

新生代:生命周期短的对象,回收频率高,回收效率高(Eden,2*survive)

新创建的对象,一般采用复制算法回收

老年代:生命周期长的对象 Tenured

经过了几轮回收的对象,一般很少需要回收,回收频率低,采用标记整理算法

永久代:方法区的对象,一般不回收 Perm

垃圾回收器:

新生代回收器

serial:单线程收集,需要暂停用户线程

parnew:serial新生代的多线程实现回收,也是暂停用户线程

parallel:

并行收集,暂停用户线程,注重吞吐量,可设置具体的吞吐量参数;吞吐量=运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

老年代回收器

serial old:同上serial

cms:老年代收集器,标记清楚,并发清除,一种获取最短回收停顿时间为目标的收集器可设置:适合用于和用户交互的业务

parallel old:同上 parallel

组合:

serial+serial old:-XX:+UseSerialGC

serial+cms

parnew+cms:-XX:+UseConcMarkSweepGC Concurrent Mark-Sweep GC

parnew+serial old:-XX:+UseParNewGC mark sweep compact gc

parallel +parallel old:-XX:+UseParallelOldGC Parallel gc with 4 thread

parallel+serial old:-XX:+UseParallelGC Parallel gc with 4 thread

g1:-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC garbage-first gc

并发,并行-cms,parallel

并发注重减少停顿

并行主动吞吐量

jvm参数设置

-Xms 初始堆大小 物理内存的1/64

-Xmx 最大堆大小 物理内存的1/4

-Xmn 年轻代大小(1.4or lator)

-XX:NewSize 设置年轻代大小(for 1.3/1.4)

-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)

-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64

-XX:MaxPermSize 设置持久代最大值 物理内存的1/4

-Xss 每个线程的堆栈大小 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K

-XX:NewRatio 年轻代与年老代的比值 4 Xms=Xmx并且设置了Xmn的情况下,该参数不需要

-XX:SurvivorRatio Eden区与Survivor区(一个)的大小比值 8

jvm参数选择

内存设置

-xms和-xmx设置为相同大小,避免垃圾回收后导致的堆区内存重新分配,根据内存设置大小4g

-xmn设置年轻代大小,一般建议为整个堆区的3/8;1536m

-xss设置每个线程的堆栈大小,默认是1m有点大,一般设置为256k;这样可以产生的线程也会多些,但是也不会超过操作系统的限制(3000-5000左)

-XX:PermSize=128m:设置适当的方法区初始大小(根据项目类信息,常量池信息设置;默认的有点小,1/64)

-XX:MaxPermSize=512m:设置适当的方法区最大大小,1/4

设置永久代最大最小一样,减少内存的重新分配512m

 

-XX:NewRatio=4:设置年轻代与年老代的比值。new:old = 1:4

-XX:SurvivorRatio=8:设置年轻代中Eden区与Survivor区的大小比值

-XX:MaxTenuringThreshold=10:设置垃圾最大年龄

 

新生代,老年代,永久代大小比例选择,默认值:

默认值:

堆:1/61,1/4

永久代:1/61,1/4

xmn:推荐整个堆的3/8,-XX:NewRatio = 2 也就是5/3~2

-XX:SurvivorRatio = 8

选择值:

新生代

响应优先:适量大小,接近系统最小响应时间(太小老收集容易进入老年代,太大收集一次太费时)

吞吐优先:尽量大,采用并行收集(快,会影响用户响应)

老年代

响应优先:适量大小,接近系统最小响应时间(太小老收集,太大一次费时)

吞吐优先:大的新生代,小的老年代

垃圾回收器选择-并发和并行的选择,cms和parallel old都是老年代

我们是应用服务器关注响应时间,交互,所以选择cms垃圾回收器

-XX:+UseConcMarkSweepGC设置parnew+cms收集器

-XX+UseCMSCompactAtFullCollection 在FULL GC的时候,对年老代的碎片整理

-XX:+CMSParallelRemarkEnabled 降低标记停顿

-XX:LargePageSizeInBytes=128m 内存页的大小不可设置过大, 会影响Perm的大小

-XX:+UseFastAccessorMethods 原始类型的快速优化

-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义初始化定义开始CMS收集 禁止hostspot自行触发CMS GC

-XX:CMSInitiatingOccupancyFraction=70 使用cms作为垃圾回收使用70%后开始CMS收集

吞吐量的垃圾回收器选择,计算,后台处理的程序;选择并行收集器增大吞吐量

-XX:+UseParallelOldGC配置并行垃圾收集器

-XX:ParallelGCThreads=8:配置并行收集器的线程数,最好与处理器数量相等

-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

 

gc日志查看

-XX:+PrintGC:简单的gc打印gc和fullgc的回收情况 ,一般不用

-XX:+PrintGCDetails:打印gc更详细的信息

打印各个区域回收前后数据,gc所占时间等等

-XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息

gc次数,各区域前后大小,会因为垃圾收集器不同而名称不同

-XX:+HeapDumpBeforeFullGC:

fullgc前保存堆快照;会造成世界停止,慎用;本地使用或者线上一台机器使用

jvm调优工具

java命令

jps:查看jvm进程(使用top或ps查找进城号会更多点)

jstack:查看jvm线程状态

state线程状态<描述状态原因>

RUNNABLE,在虚拟机内执行的。

BLOCKED,受阻塞并等待监视器锁。

WATING,无限期等待另一个线程执行特定操作。

TIMED_WATING,有时限的等待另一个线程的特定操作。

jinfo:查看jvm配置

jstat:内存,垃圾回收统计信息

jmap:堆内存信息,对象信息,垃圾回收器

jhat:分析工具,分析jmap生成的堆快照

visual vm:集成各个jvm命令的可视化界面

功能更方便好用了

怎么查看gc类型

jinfo查看jvm参数设置,查看对应设置的哪一个收集器

jmap -heap查看堆信息

jvm 版本

回收器如:Concurrent Mark-Sweep GC,这个就是并发收集器

堆内区域使用信息,大小信息

jvm问题调试实例

cpu持续过高:

top查看cpu占用率高的进程

top -Hp pid 查看进程下占用cpu较高的进程tid(转为16进制,对应jstack的nid)

jstack 查看过滤线程信息,查看cpu占用率较高时间持续较长的线程的状态

结合线程状态和程序代码分析问题

 

过高几种情况

持续gc,多个gc线程runnable

大量线程死循环runable

内存占用过高

top查看内存占用率高的进程

jstat -gccause(jmap -heap)查看内存使用情况和分布情况

jmap -histo 分析内存中对象数量和内存大小

分析异常的对象数量和内存,根据代码查找问题

 

查看进程下线程数量是否异常过多,查看进程下的线程数:ps -hH 19058 | wc -l

查看那些线程过多线程种类,是否正常,状态是否正常:jstack

 

内存溢出与泄漏:

溢出:内存不够了

对象创建过多

内存太小

泄露:内存没有及时回收导致程序问题

长期对象持有短期对象引用(静态对象持有)

资源对象的释放(db连接,io)

对象的监听器释放

响应时间:

查看程序处理,调用日志

查看内存和cpu负载状态

一般都是cpu过高导致,查看cpu使用情况就行,很可能是用户线程死循环或者gc线程

gc原因:

查看线程状态,gc统计次数(jstat -gc)

根据线程状态找出异常线程

如果是gc线程,查看gc次数,和gc原因

gc原因:一般是有大量的临时对象被创建,还是要去查看客户自己的线程和代码情况,看哪些线程一直在运行且产生了临时对象

 

进程崩溃调优:

gc日志

-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件,jhat分析

包含对信息:查看实例数比较多的类对象,是否合理,为什么没被回收

堆转储上的线程信息:查看出现异常时各个线程的状态

一般是内存溢出,查看内存对象个数,查看是否发生内存泄漏,处理泄漏

查看gc是否频繁,为啥频繁?

jstat -gccause pid :查看最近两次gc的原因,一般是allocation failure(分配对象内存不足时)

查看哪个线程,哪个类分配什么对象时不足?

查看快照分析数量较多的对象

查看线程正在运行的任务,是否会导致一直生产大量对象

比较两个时间的堆对象变化

找不到的话?

开启XX:+HeapDumpBeforeFullGC;由于它会导致世界暂停所以我们慎用,(本地使用调优或者线上一台服务器开启)

查看fullgc前的堆快照信息,看什么对象占比大,和gc后的对比看哪些对象不应该大量存在老年代,导致fullgc

 

gc多少算频繁(一般是fullgc导致性能问题)

full gc基本不要有,或者几天一次

young gc:十秒一次就行

fullgc时机:老年代,永久代不够

大对象新生代放不下,直接入老年代,老年代也放不下就fullgc

持久代(永久代)空间不足

新生代对象到老年代,老年代不足

minorgc时机:

新生代不够,判断老年代与新生代所有对象大小

大于,直接minorgc

小于,判断是否开启HanlerPromotionFailure(是否允许失败)

开启,判断历次minorgc之后对象平均值

大于:fullgc

小于:minorgc

没开启:直接fullgc

heap扩容时机:fullgc之后heap还不够

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值