JVM一些知识

一、JVM概念

JVM:Java Virtual Machine的简称,即为java虚拟机。

虚拟机:指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。

常用的虚拟机有VMWare、Visual Box、JVM等。

VMWare、Visual Box是使用软件模拟物理的CPU指令集,JVM模拟字节码指令集。广泛使用的JVM是HotSpot。

JDK版本:JDK1.2开始 称为Java 2,J2SE J2EE J2ME 出现,JDK加入了Swing ,Collections等内容。若要在JVM运行,则需要满足JVM规范。

能在JVM运行的语言不仅有Java,还有Scala,Clojure,Groovy等等。但JVM中许多规范最初还是为Java设计的。

二、运行机制

JVM启动流程:见JVM附录图

JVM基本结构:见JVM附录图

PC寄存器:指向下一条指令的地址,在创建每个线程时都对应创建寄存器。执行本地方法时,其值为undefined。

方法区包含:

装载的类信息

类型的常量池(JDK7中String等常量信息移到堆中)

字段,方法信息

方法字节码

通常和永久区关联在一起

Java堆:所有的对象都存在堆中,被所有的线程共享。堆是分区的,分为eden(伊甸园区)、s0和s1(幸存区)、tenured(老年区)。

Java栈:

线程私有,栈由一系列帧组成。

每一次方法调用时会创建一个帧,并压栈,在方法调用结束时,回收帧。

帧保存一个方法的局部变量、操作数栈、常量池指针。

栈上分配:对于小对象,可以直接在栈上分配,减轻GC压力。但大对象不能在堆上分配。

栈、堆、方法区交互 :见JVM附录图

内存模型:每个线程都有一个线程内存和一个主内存。线程内存中存放的是主内存中值的拷贝 : 见JVM附录图

三、JVM配置参数

Trace跟踪参数:

-verbose:gc

-XX:+printGC 可以打印GC的简要信息

-XX:+PrintGCDetails 打印GC详细信息

-XX:+PrintGCTimeStamps 打印CG发生的时间戳

-Xloggc:log/gc.log 指定GC log的位置,以文件输出

-XX:+PrintHeapAtGC 每次一次GC后,都打印堆信息

-XX:+TraceClassLoading 监控类的加载

堆的分配参数:

-Xmx –Xms  指定最大堆和最小堆

-Xms(M > 1G ? 1G/64 : M/64)  , -Xmx(M > 1G ? 1G/4 : M/4) 例如:-Xmx20m -Xms5m  最大堆为20m,最小堆为5m

-Xmn 设置新生代大小

-XX:NewRatio 老年代(不包含永久区)和 新生代(eden+2*s)的比值

   例如:4 表示 老年代:新生代=4:1,即新生代占堆的1/5

-XX:SurvivorRatio 设置eden和两个Survivor区的比

   8表示 eden:两个Survivor =8:2,即一个Survivor占新生代的1/10

(官方推荐新生代占堆的3/8,幸存代占新生代的1/10)

-XX:+HeapDumpOnOutOfMemoryError OOM时导出堆到文件

-XX:+HeapDumpPath 导出OOM的路径

-XX:PermSize  -XX:MaxPermSize 设置永久区的初始空间和最大空间  

永久区溢出会抛出OOM

-Xss:设置栈大小

通常只有几百K

决定了函数调用的深度(递归深度)

每个线程都有独立的栈空间

局部变量、参数 分配在栈上

四、GC算法与种类

GC:Garbage Collection 垃圾收集。Java中不需手动释放对象内存,通过垃圾回收机制自动实现。

Java中,GC的对象是堆空间和永久区。

GC算法:

(1)引用计数法:每个对象对应一个引用计数器。当其它对象引用该对象时,计数

器加1 ,引用失效(如将对象置为null ,obj = null)时减1,当计数器的值为0时,

表示这个对象是可回收的对象。

缺点:不能解决对象循环引用的情况,Java中不使用。

(2)标记清除法:分两个阶段,标记阶段和清除阶段;

从根节点出发,可到达的对象标记为可达对象。没有被标记的即表示为

未被引用的垃圾对象。在清除阶段,即可删除把未被标记的对象清除。

(3)标记压缩法:对标记清除法的优化。标记对象后,将存活的对象压缩到内存的一端。

(4)复制算法:将内存分为两块,每次只使用其中一块。垃圾回收时,将正在使用

的内存中的存活对象复制到未使用的内存中,然后把正在使用的内存中的所有对象

清除,最后交换两块内存的角色。(在其中一块查找存活对象时,可使用标记清除法)

少量对象存活,适合复制算法 ,比如幸存代

大量对象存活,适合标记清理或者标记压缩,比如老年代

对象存活周期进行分类,短命对象归为新生代,长命对象归为老年代

所有的算法,需要能够识别一个垃圾对象,才能进行垃圾回收。

可触及性:

 (1)可触及的:从根节点可以触及到这个对象

 (2)可复活的:一旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象

 (3)不可触及的:在finalize()后,可能会进入不可触及状态(避免使用finalize() )

     不可触及的对象不可能复活,可以回收

Stop-The-World

Java中一种全局暂停的现象

全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互

多半由于GC引起

Dump线程

死锁检查

堆Dump

危害

长时间服务停止,没有响应

可能引起主备切换,严重危害生产环境。

GC垃圾回收器:

串行回收器:效率高,可能会产生较长的停顿  ,使用方法 -XX:+UseSerialGC

并行回收器:多线程执行,

使用方式:

      -XX:+UseParNewGC(新生代并行,老年代串行)

      -XX:+UseParallelGC (新生代、老年代并行)

      -XX:+UseParallelOldGC (老年代并行、新生代串行)

CMS收集器:Concurrent Mark Sweep 并发标记清除(并发、标记结合使用)

使用方式:-XX:+UseConcMarkSweepGC

五、类加载器

若需要执行Java代码,则需要将代码加载到JVM的内存中,所以需要类加载器.类通常是按需加载,即第一次使用该类时才加载。

类加载流程:加载、链接(验证、准备、解析)、初始化

加载:取得类的二进制流,转为方法区数据结构

       在Java堆中生成对应的java.lang.Class对象

链接 -> 验证:保证Class流的格式是正确的。包括文件格式的验证、元数据验证、

字节码验证、符号引用验证。

链接 -> 准备:分配内存,并为类设置初始值 (方法区中)

链接 -> 解析:符号引用替换为直接引用

初始化:执行父类构造函数,父类的static变量 赋值语句

   static{}语句,然后再执行子类的构造函数,子类的static变量 赋值语句

   static{}语句。

ClassLoader是一个抽象类,它的实例将读入Java字节码将类装载到JVM中。

可以定制,满足不同的字节码流获取方式。

ClassLoader分类:

BootStrap ClassLoader (启动ClassLoader,加载 rt.jar下的类)

Extension ClassLoader (扩展ClassLoader,加载 ext/*.jar下的类)

App ClassLoader (应用ClassLoader/系统ClassLoader ,加载Classpath下类)

Custom ClassLoader(自定义ClassLoader,加载自定义路径下的类)

ClassLoader默认使用双亲委派机制,即某个特定的类加载器在接到加载类的请求

时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加

载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

ClassLoader自底向上检查是否类已经加载,然后自顶向下尝试加载类。

tomcat加载类是先从底层ClassLoader加载的。

六、性能监控工具

系统性能监控

    Linux环境下:

     uptime : 查看系统时间、(1,5,15分钟内的系统平均负载)、运行时间等

     top:动态查看进行运行情况

     vmstat:查看CPU、内存、Swap等使用信息

     pidstat:查看进程信息

    Windows环境下:

     任务管理器

     自带的Perfmon

     pslist:可显示指定程序运行情况 如:pslist javaw

Java自带工具

     jps:列出java进程

     jmap:生成Java应用程序的堆快照和对象的统计信息

     jstack:打印线程信息,可以找出死锁信息

     JConsole:图形化监控工具,可以查看Java应用程序的运行概况,监控堆信息、永久区使用情况、类加载情况等

Visual VM :是一个功能强大的多合一故障诊断和性能监控的可视化工具

七、Java堆分析

JVM使用内存空间的地方:堆、永久区、线程栈、直接内存。

内存溢出:内存不够,程序所需的内存超出了JVM内存所承受大小。

(1)堆溢出:增大堆空间,及时释放内存

(2)永久区生成大量的类:增大Perm区,允许Class回收

(3)栈溢出:减少线程栈大小

分析内存溢出:

(1)使用Memory Analyzer(MAT)工具分析

(2)Java自带工具分析

八、锁

使用锁,使多线程的访问时数据保持唯一性和准确性,与synchronized具有相同的效果。

对象头Mark:描述对象的hash、锁信息,垃圾回收标记,年龄

偏向锁:锁会偏向于当前已经占有锁的线程。在竞争不激烈时,可以通过偏向来提高

    性能。使用方式:将对象头Mark的标记设置为偏向,并将线程ID写入对象头Mark,-XX:+UseBiasedLocking。

轻量级锁:普通的锁处理性能不够理想,轻量级锁是一种快速的锁定方法。

自旋锁:采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时才能进入临界区。

内置于JVM中的获取锁的优化方法和获取锁的步骤

偏向锁可用会先尝试偏向锁

轻量级锁可用会先尝试轻量级锁

以上都失败,尝试自旋锁

再失败,尝试普通锁,使用OS互斥量在操作系统层挂起

锁优化:减少锁持有时间、减小锁粒度(将大对象拆成小对象)、锁分离(如读锁写

锁分离)、锁粗化(在一些时候可扩大同步块范围)、锁消除(对不可能存在共享数据

竞争的锁进行消除)、无锁(某些时候可以不用锁)

九、Class文件结构

语言无关性:class文件内部定义了虚拟机可以识别的字节码格式,这个格式是

   平台无关性的,在linux系统或者在windows系统上都是一致的,与语言也无关的。

Class文件结构是基于字节流的,用unicode进行编码

Class文件结构:

魔数

版本

常量池

访问符

类、超类、接口

字段

方法

                           属性

十、字节码执行

javap:Class文件反汇编工具

常用的字节码:常量入栈、局部变量压栈等等

使用ASM生成Java字节码:Java字节码操作框架,可以用于修改现有类或者动态产生新类

JIT:字节码执行性能较差,所以可以对于热点代码编译成机器码再执行,在运行时的编译。

JIT的基本思路是,将热点代码,就是执行比较频繁的代码,编译成机器码

字节码执行过程:常用的字节码:常量入栈、局部变量压栈等等

用ASM生成Java字节码:Java字节码操作框架,可以用于修改现有类或者动态产生新类

JIT:字节码执行性能较差,所以可以对于热点代码编译成机器码再执行,在运行时的编译。

JIT的基本思路是,将热点代码,就是执行比较频繁的代码,编译成机器码

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值