640 likes | 857 Views
Writing Efficient Java Series (B2) 深入 JVM 虚拟机 ( 下 ). 2013 . 9 共享软件开发及管理部 季怡. 内容. 基础课程 A1 J2SE 基础 从 String 开始 集合与数组 A2 日志与 JDBC 正确使用日志 JDBC 的操作优化 A3 程序性能分析与优化 分析与评估程序性能 常用优化手段 A4 编程定式. 进阶课程 B1 深入 JVM ( 上 ) 理解 JVM 执行引擎 Java 的编译期处理 B2 深入 JVM ( 下 ) 编译期优化的利用 动态硬编码技术
E N D
Writing Efficient Java Series (B2) 深入JVM虚拟机(下) 2013.9 共享软件开发及管理部季怡
内容 基础课程 • A1 J2SE基础 • 从String开始 • 集合与数组 • A2 日志与JDBC • 正确使用日志 • JDBC的操作优化 • A3 程序性能分析与优化 • 分析与评估程序性能 • 常用优化手段 • A4 编程定式 进阶课程 • B1 深入JVM (上) • 理解JVM执行引擎 • Java的编译期处理 • B2 深入JVM (下) • 编译期优化的利用 • 动态硬编码技术 • JVM运行期优化 • B3 高性能并发 • B4 故障分析与诊断
usega of byte-code/compiler optimization 字节码技术和编译优化的利用
开发者能做的 • 理解机制 减少不必要的性能开销 • 从一个开源项目说起: • fastjson ——https://github.com/alibaba/fastjson • 阿里巴巴 温绍锦 • 比所有json工具快,比google的protocol buf等二进制快
近况 • 随着稳定性和适应范围的扩大,性能略有下降。 • 部分极端场合还有性能BUG
Fast-json的核心技术 • 高效文本解析器 • 基于动态字节码技术实现硬编码
硬编码的性能提升 • 使用“硬编码”后—— • 不再有反射开销 • 循环已被展开,不再有开销 • 部分变量转为常量 • 没有随机访问 • 动态“硬编码”技术 (字节码技术) • 第一次执行时 • 为每个User Bean生成专用的序列化、反序列化类
细节处理 • 方法内联 • fast-json中的很多方法都是非常长的,相当于人工执行了一些方法内联 • 自行编写的并发实现 • final关键字的使用 • 减少对象创建 • ……
FastJson为什么快 今天已经打下了基础 回去可以和小伙伴一起阅读fast-json源码了!
JVM如何找寻方法 • 静态分派 • 编译期间计算方法重载的版本 • 找寻”最合适的”方法 • 动态分派 • 根据编译产生的“方法签名” • 找寻方法的接收者
变量和方法尽可能private 性能逐渐下降
分支处理 • if … else if … else …模型中 • 将出现概率高的分支放在前面 • 在可读性条件允许的范围内 • 原因:if else模型是顺序执行的 • switch不是,因此不适用此规则
能不用的分支就不要用 有些人喜欢用分支为boolean变量赋值
分支控制 switch的编译实现 • switch有两种实现 • 逐条判断? 我的switch不可能那么笨 • 选择哪种实现由编译器基于优化规则处理
tableswitch 的可能实现 9: invokevirtual #38; //Method java/lang/Thread$State.ordinal:()I 12: iaload 13: tableswitch{ //1 to 6 1: 52; 2: 74; 3: 74; 4: 74; 5: 74; 6: 63; default: 82 } 52: getstatic #44; //Field java/lang/System.out:Ljava/io/PrintStream; 55: ldc #50; //String 线程还没运行。 先获取分支范围 按跳转值作为偏移量 直接到表中获取下一条指令的地址 switch为什么只能用int型做跳转参数, 这样就可以理解了。
lookupswitch的可能实现 11: invokevirtual #91; //Method java/lang/String.hashCode:()I 11: lookupswitch{ //3 70499: 44; 74231: 56; 77118: 68; default: 86 } 44: aload_2 45: ldc #96; //String Feb 47: invokevirtual #98; //Method java/lang/String.equals:(Ljava/lang/Object;)Z 将分支条件放入下列适合随机访问的数据库结构中,运行时得到下一指令的入口 跳表结构查询复杂度:O(log n) 哈希表结构 查询复杂度: O(1) ——无碰撞下
字节码技术 • 实现“硬编码”(高性能计算) • 在运行期间,动态生成代码的技术 • 针对运行期业务场景,固化业务逻辑。 • java计算性能的终极解决办法 • 查找和分析类 • ClassScanner • 动态代理 / AOP • Entity Enhancer
CGLib bean copier • net.sf.cglib.beans.BeanCopier • org.springframework.cglib.beans.BeanCopier 秒杀: org.apache.commons.beanutils.BeanUtils.copyProperties(Object, Object) org.apache.commons.beanutils.PropertyUtils.copyProperties(Object, Object) org.springframework.beans.BeanUtils.copyProperties(Object, Object) org.springframework.beans.BeanWrapper + 循环赋值
Javasist示例 在类的每个set方法最前面植入代码,去调用prepareSet()方法
Why ASM • 最基础 • 别的框架能做的,ASM一定能做。ASM能做的,别的框架不一定能做。 • 更轻,更小,更快 • byte code其实很简单 • 中文资料和教程越来越多 ASM核心类只有15~20个,代码400K。不依赖任何第三方库
大量的框架都内嵌了ASM并使用 • 为了让自己的框架看上去更独立?! • 避免和其他框架冲突 • 避免版本兼容问题
ASM中方法和字段的修饰 • 描述一个方法或字段
ASM ClassReader • 可以读取(解析class文件) • 被ClassVisitor所访问
示例一 • EntityScanner
ASM ClassWriter • ClassWriter也是一个访问者 • visitMethod (MethodVisitor) 可以生成代码 • visitField (FieldVisitor) • visitSource • visitInnerClass • 如果用new ClassReader(out.data).accept(new ClassWriter());就相当于在ClassWriter中复制了原始类的全部结构
ASM的装箱和拆箱实现 var.TypeValue() “()I” “intValue” Type.valueOf() “(I)Ljava/lang/Integer;” “java/lang/Integer”
Runtime Opmization JVM运行期优化
HotSopt中 • 一个 byte code Interpreter • 解释执行字节码 • 两个JIT Compiler • C1 (client)编译器 • 轻量级、编译时间短、占用内存少 • 使用前端程序 • C2 (server)编译器 • 重量级、执行效率高、大量编译优化 • 适用服务器
解释器与编译器 即时编译 C1 编译器 解释器 Client Compiler Interperter C2 Server Compiler 逆优化
JIT的历史 Dalvik VM Google
HotSpot的工作模式 • -server • -client • -Xint • -Xcomp • -d64 • HP unix等一些环境下的JVM,用-d64或-d32来指定使用32位JVM还是64位JVM
Server VM和Client VM • 就只有编译器(策略)的区别
查看编译器工作状态 • -XX:+PrintCompilation • -XX:+PrintInlining • -XX:+PrintAssembly 38 1 java.lang.String::hashCode (64 bytes) 39 2 java.lang.String::indexOf (151 bytes) 39 3 sun.nio.cs.ext.DoubleByteDecoder::decodeSingle (10 bytes) 39 4 java.lang.String::charAt (33 bytes) 39 5 java.lang.String::lastIndexOf (156 bytes) 40 6 java.io.Win32FileSystem::normalize (143 bytes) 40 7 java.lang.String::indexOf (166 bytes) 41 8 java.lang.AbstractStringBuilder::append (40 bytes) 42 9 java.io.Win32FileSystem::isSlash (18 bytes) 42 10 s java.lang.StringBuffer::append (8 bytes) 42 11 java.lang.Object::<init> (1 bytes) 43 12 sun.misc.ASCIICaseInsensitiveComparator::compare (143 bytes) 43 13 java.io.Win32FileSystem::normalize (231 bytes) 44 14 java.lang.StringBuilder::append (8 bytes) 44 15 ! sun.net.www.ParseUtil::decode (194 bytes) 45 16 java.lang.CharacterDataLatin1::getProperties (11 bytes) 45 17 java.lang.String::toLowerCase (436 bytes) 46 18 java.lang.Character::toLowerCase (162 bytes) 46 19 java.lang.CharacterDataLatin1::toLowerCase (36 bytes) 46 20 java.lang.String::replace (142 bytes)