深入理解JVM
classLoader加载.class完成后,有两种执行方式:
解析执行
编译机器码执行:client和server两种模式
Java代码执行机制
编译为class文件
Java源码编译机制
A分析和输入的符号表(parseandenter)
Pase词法和说法分析
Enter符号输入到符号表
B处理注解(annotationprocessing)
C语义分析和生成class文件(analyseandgenerate)
类加载机制:
装载(load)全限定名+类加载器
链接(link)
初始化(initialize):
调用了new
反射调用类中的方法
子类调用了初始化
Jvm启动过程中指定的初始化类
类加载器有四种:
BootstrapclassLoader
ExtensionClassLoader加载扩展功能的一些jar包
SystemClassLoader加载启动参数中指定的classPath中的jar包和目录
User-DefinedClassLoader(用户自定义类加载器)
classLoad提供的几个关键方法
loadClass()加载指定类名的方法
查找顺序为:ClassLoader先从已加载的类中--------->parentclassLoad
------------>systemClassLoad---------->最后覆盖findClass来作
特殊处理
findLoadedClass是从当前classLoader实例对象的缓存中查询已加载的类,
调用的是本方法
findClass此方法直接抛出classNotFoundException
findSystemClass从systemclassLoader如果未找到继续从bootstrap
classLoader中寻找,未找到返回空
defineClass将二进制字节码转换为字节对象
resolveClass完成class对象的链接,如果链接过则直接返回
类加载过程中常抛出的异常
classNotFoundException未找到类文件
noClassDefFoundError加载类中引用的另外的类不存在
linkageError在用户自定义加载器中容易出现,此类已经在classloader中加载过
了,重复加载会出此异常
classCastException类型转换异常
类执行机制
字节码解释执行
执行方法的指令:
Invokestatic调用静态方法
Invokevirtual调用对象实例的方法
Invokeinterface调用接口
Invokespecial调用private方法和编译原码后生成的init方法
Javac编译代码
Javap-c查看字节码
SunJDK基于栈的体系结构来执行字节码
示例代码如下:
Java代码
publicclassDemo{
publicstaticvoidfoo(){
inta=1;
intb=2;
intc=(a+b)*5;
}
}
//字节码如下
publiccn.yue.distributed.esb_soa.Demo();
Code:
0:aload_0
1:invokespecial#1;//Methodjava/lang/Object."<init>":()V
4:return
publicstaticvoidfoo();
Code:
0:iconst_1//将类型为int,值为1的常量放入操作数栈
1:istore_0//将操作数栈中的栈顶的值弹出放入局部变量区
2:iconst_2//同上
3:istore_1
4:iload_0//装载局部变量区中第一个值到操作数栈
5:iload_1//同上装载第二个操作数
6:iadd//执行int类型的add指令并将计算出的结果放入操作数栈
7:iconst_5//将类型为int,值为1的常量放入操作数栈
8:imul//执行int类型的mul方法,将计算出的结果放入操作数栈
9:istore_2//将操作数栈中弹出放入局部变量
10:return//返回
}
指令解释执行(冯诺一曼体系中的FDX循环方式)
获取一条指令,解码分派,然后执行
实现FDX循环时有
switch-threading
示例代码:
While(true){
Intcode=fetchNextCode();
Switch(){
CaseIADD:
//dosomething
Case...:
//dosomething
}
}
Tooken-threading
示例代码如下;
IADD:{
//doadd;
fetchNextCode();
Dispatch();
}
Iconst_0:{
Push{0};
fetchNextCode();
Dispatch();
}
Dircet-threading
Subroutime-threading
Inline-threading
栈顶缓存
位于操作数栈顶的值放往寄予存器
部分栈帧共存
一个方法调用另一个方法,传到另一个方法的参数已存放在操作数栈的数据
SunJDK作了一个优化,就是当调用方法时,后一方法可将前一方法的操作数栈
作为当前方法的局部变量
编译执行
为提升代码的执行性能,JDK提供将字节码编译为机器码的支持,编译在运行时执行
通常为JIT编译器
提供的两种模式为:
clientcompiler
JDK1.6以后采用的是线性扫描寄存器分配算法
其他方法的优化:
方法内联
示例代码如下
Publicclassdemo1(){
....
demo2();
....
}
Publicclassdemo2(){
}
//如果编译后demo2的字节数小于35个字节
会用类以于以下的结梦
Publicclassdemo1(){
//demo2
Publicclassdemo2(){
}
}
可以在JDK的启动参数上加-XX:+PrintlnLining
去虚拟化
在装载class文件后,如果类中的方法只提供一个实现类。对于调用了此方法的代码,也可以进行方法内联
冗余削除
在编译时,根据运行时情况进行代码折叠或削除
privatestaticfinalLoglog=LogFactory.getLog("");
privatestaticfinalbooleanisDebug=log.isDebugEnable();
publicvoidexecute(){
if(isDebug){
//dosth
}
//dosomething
}
如果isDebugEnabled返回的为false
那么以上方法就变为:
publicvoidexecute(){
//dosomething
}
Servercompiler图着色寄存器算法(逃逸分析是进行优化的基础)
标量替换
栈上分配
同步消除
反射执行
Classclazz=Class.forName("类名");
Methodmethod=clazz.getMethod("方法名",null);
Objectobject=clazz.newInstance();
method.invoke(object,null);
Jvm内存管理
方法区存放了要加载的类的信息最小值为16m,最大值为64m
堆:存储对象实例和数组值,也可以理解为new出来的对象
32位操作系统上最大为2G,64位机器上没有限制
新生代(newgeneration):大多数新建的对象从新生代分配内存
旧生代(oldgeneration):用于存放新生代中多次垃圾回收仍然存在的对象
本地方法栈支持本地方法的执行
Pc寄存器和JVM方法栈每个线程都会创建PC寄存器和JVM方法栈
Pc寄存器占用的或操作系统内存
JVM占用操作系统内存
当空间栈空间不足时,会抛stackOverFlowError
内存分配
Java对象占用的内存主要从堆上分配,堆是线程共享的,在分配内存时要进行加锁
为每个新建线程分配一块独立的空间(TLAB)threadlocalallocationbuffer
内存回收
收集器
引用计数收集器
跟踪收集器
实现方法
复制
标记-清除
标记-压缩
JDK中可用的GC
新生代可用GC
未看完
JVM内存状况查看和分析工具
A输出gc日志
输出到控制台:
在启动参数中:-XX:+PrintGC-XX:+PrintDetails-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
输出到指定文件:
-verbose:gc-XX:+PtingTenuringDistribution
BGCPortal
CJConsole
DJVisualVM
EJMap
FJStat
GEclipseMemoryAnalyzer
JVM线程资源同步及交互机制
线程资源同步机制示例代码如下
inti=0;
publicintgetNextId(){
returni++;
}
以上代码的执行的步骤为:
首先在mainmemory(java堆中)给i分配内存,并存0;
线程启动后,会分配一片workingmemory(操作数栈)
i++执行的步骤为:装载读取进行i+1操作,存储i写入i
线程交互机制
线程状态分析
查看线程状态
Kill-3[线程id]将线程的相关信息输出到控制台
Jstack可以直接查看线程的运行状况
JConsole工具
threadXMBean
TDA
序列化和反序列化
性能调优
Cpu消耗分析
Linux中cpu用于中断、内核、用户进程的任务处理
上下文切换
每个cpu只能执行一个线程采用抢占式调用
运行队列控制每个cpu核上运行队列为1-3个
利用率
可以通过top查看进程中的CPU的消耗状态
Pidstat是systat中的工具,需先安装SYSTAT
Cpu的消耗主要体现在两个方面上ussy
文件IO消耗分析
跟踪线程的文件IO的消耗
方法一:
Pidstat-d-t-p[pid]100类似的命令可以查看线程IO的消耗状况
方法二:
Iostat
网络IO消耗分析
Google修改kernel方法对网卡中断不均的问题进行修复,或是用支持MSI-X的网卡来修复
查看网络IO消耗状况
Sar-nFULL12
内存消耗分析
通过vmstatsartoppidstat等方式来查看swap和物理内存消耗状况
Vmstat和内存相关的信息其中swpd是大小说明物理内存不够用,将数
据放到硬盘上的数据的大小
Sar的-r参数可以查看内存消耗状况,可以查询历史状况
以上两者不能分析进程所占用的内存量
Top可以查看所消耗的内存量
Pidstatpidstat-r-p[pid][interval][times]
对物理内存的消耗
实现对物理内存的直接操作
publicstaticvoidmain(String[]args)throwsInterruptedException{
Thread.sleep(20000);
System.out.println("readtocreatebytes,soJVMheepwillbeused");
byte[]bs=newbyte[128*1000*1000];
bs[0]=1;
bs[1]=2;
Thread.sleep(10000);
System.out.println("readtoallocate&putdirectbytebuffer,noJVMheepshouldbeused");
ByteBufferbyteBuffer=ByteBuffer.allocate(128*1024*1024);
byteBuffer.put(bs);
byteBuffer.flip();
Thread.sleep(10000);
System.out.println("readtogc,JVMheepwillbefreed");
bs=null;
System.gc();
Thread.sleep(10000);
System.out.println("readtogetbs,thenJVMheapwillbiused");
byte[]resultbytes=newbyte[128*1000*1000];
byteBuffer.get(resultbytes);
System.out.println("resultbytes[1]is:"+resultbytes[1]);
Thread.sleep(10000);
System.out.println("readtogcall");
byteBuffer=null;
resultbytes=null;
System.gc();
Thread.sleep(10000);
}
程序执行慢原回分析
锁竟争激烈
未充分使用硬件资源
数据量增长jprofiler可以查看记录程序的时间消耗
性能调优
可以从硬件操作系统JVM程序
JVM调优
代大小的调优
在不采用G1的情况下通常minorGC会快于fullGC
各个代大小的设置决定了minorGC和fullGC触发的时机
程序调优
Cpuus高的解决方法
添加thread.sleep(),以释放CPU的执行权
Cpusy高的解决方法
减少线程数
网络IO并发操作,可以采用协程(coroutine)来支撑更高的并发量
实现协程的框架为killim
文件IO消耗严重
异步写文件
批量读写
限流
限制文件大小
网络IO消耗
释放不必要的引用
使用对象缓存池
采用合理的缓存失效算法建立缓存池FIFOLRULFU
分享到:
相关推荐
### 实现分布式Java应用的挑战与解决方案 尽管Java提供了丰富的库和技术栈来支持分布式应用的开发,但在实际应用中仍面临许多挑战,例如: - **一致性问题**:在分布式系统中保持数据的一致性是一项复杂任务,需要...
从给定的文件信息来看,标题和描述都指向了“Java分布式学习笔记01分布式Java应用”,这显然是关于Java在分布式环境下的应用和技术的学习资料。虽然提供的部分内容由于格式问题难以直接解析,但我们可以根据标题、...
Java分布式应用学习笔记 在Java世界中,分布式应用是指由多个独立组件通过网络通信协同工作的系统。这种架构模式常用于构建大规模、高可用性、可扩展的系统。本笔记将深入探讨Java分布式应用的核心概念、技术和实践...
### Java分布式应用学习笔记07线程池应用 在深入探讨Java分布式应用中线程池的应用之前,我们先来理解一下线程池的基本概念及其在并发编程中的重要性。线程池是Java并发编程的核心技术之一,它通过复用一组预创建的...
分布式系统和SOA的介绍与应用 前言 随着系统规模的扩大,分布式架构的应用变得越来越广泛。...在Java领域,分布式应用的学习和实践是一个非常重要的方向,这对于理解和掌握现代企业级应用架构具有重要意义。
【Java分布式应用学习笔记-谈JVM】 在Java分布式应用中,JVM(Java虚拟机)扮演着至关重要的角色。虽然有些人可能认为分布式系统与JVM的关系并不密切,但事实上,尤其是在大型分布式环境,如云计算服务平台,对Java...
### Java分布式应用学习笔记09JMX-MBean的介绍 #### MBean概念及作用 MBean,即Managed Bean,是在JMX(Java Management Extensions)框架中用于管理资源的一种特殊Java对象。通过MBean,可以方便地对应用程序进行...
本篇笔记将深入探讨Java中的并发同步机制,包括核心概念、工具类以及在实际开发中的应用。 首先,我们要理解什么是线程安全。线程安全是指在多线程环境下,一个方法或类能够正确处理多个线程同时访问的情况,不会...
### Java分布式应用学习笔记06浅谈并发加锁机制分析 #### 1. 前言 在深入探讨Java中的并发加锁机制之前,我们有必要回顾一下多线程环境下的一些基本概念和技术。之前的多线程调度、并发调度以及线程加锁安全等内容...
### Java分布式应用学习笔记05多线程下的并发同步器 #### 1. 前言 在现代软件开发中,特别是在分布式系统和高性能计算领域,有效地管理多线程之间的协同工作至关重要。Java语言提供了丰富的工具和API来帮助开发者...
Java Management Extensions (JMX) 是Java平台上的一个标准,它定义了一种管理和监控Java应用程序的...无论是简单的本地程序还是复杂的分布式系统,JMX都是一个强大的工具,帮助我们更好地理解和控制我们的Java应用。
Java学习笔记 Java是一种流行的编程语言,广泛应用于Android应用程序开发、Web应用程序开发、桌面应用程序开发等领域。以下是Java学习笔记的摘要信息: 一、Java技术基础 * 1.1 编程语言:Java是一种面向对象的...
### Java分布式应用学习笔记03:JVM对线程的资源同步和交互机制 在深入探讨Java虚拟机(JVM)如何处理线程间的资源同步与交互机制之前,我们先来明确几个关键概念:线程、多线程、同步、并发以及它们在Java中的实现...
【分布式技术相关知识学习笔记】 分布式技术是现代软件开发中的重要组成部分,它涉及多种技术手段,如CORBA、ORB、RPC、RMI以及中间件等,旨在解决大型系统中复杂度、扩展性和高可用性的问题。本笔记将重点讨论EJB...
【Java分布式高级架构师课程学习笔记】 在Java分布式高级架构师的学习过程中,涵盖了多个关键领域的技术,包括数据库的高可用方案、缓存系统、消息中间件以及分布式协调服务。以下是对这些主题的深入探讨: 1. **...
java