目录
1 实验目标概述 1
2 实验环境配置 1
3 实验过程 1
3.1 Static Program Analysis 1
3.1.1 人工代码走查(walkthrough) 1
3.1.2 使用CheckStyle和SpotBugs进行静态代码分析 1
3.2 Java I/O Optimization 1
3.2.1 多种I/O实现方式 1
3.2.2 多种I/O实现方式的效率对比分析 2
3.3 Java Memory Management and Garbage Collection (GC) 3
3.3.1 使用-verbose:gc参数 3
3.3.2 用jstat命令行工具的-gc和-gcutil参数 3
3.3.3 使用jmap -heap命令行工具 3
3.3.4 使用jmap -clstats命令行工具 3
3.3.5 使用jmap -permstat命令行工具 3
3.3.6 使用JMC/JFR、jconsole或VisualVM工具 3
3.3.7 分析垃圾回收过程 3
3.3.8 配置JVM参数并发现优化的参数配置 3
3.4 Dynamic Program Profiling 3
3.4.1 使用JMC或VisualVM进行CPU Profiling 3
3.4.2 使用VisualVM进行Memory profiling 3
3.5 Memory Dump Analysis and Performance Optimization 3
3.5.1 内存导出 3
3.5.2 使用MAT分析内存导出文件 3
3.5.3 发现热点/瓶颈并改进、改进前后的性能对比分析 3
3.5.4 在MAT内使用OQL查询内存导出 4
3.5.5 观察jstack/jcmd导出程序运行时的调用栈 4
3.5.6 使用设计模式进行代码性能优化 4
4 实验进度记录 4
5 实验过程中遇到的困难与解决途径 4
6 实验过程中收获的经验、教训、感想 5
6.1 实验过程中收获的经验和教训 5
6.2 针对以下方面的感受 5
1实验目标概述
本次实验通过对 Lab4 的代码进行静态和动态分析,发现代码中存在的不符
合代码规范的地方、具有潜在 bug 的地方、性能存在缺陷的地方(执行时间热点、
内存消耗大的语句、函数、类),进而使用第 4、7、8 章所学的知识对这些问题
加以改进,掌握代码持续优化的方法,让代码既“看起来很美”,又“运行起来
很美”。
具体训练的技术包括:
? 静态代码分析(CheckStyle 和 SpotBugs)
? 动态代码分析(Java 命令行工具 jstat、jmap、jcmd、VisualVM、JMC、
JConsole 等)
? JVM 内存管理与垃圾回收(GC)的优化配置
? 运行时内存导出(memory dump)及其分析(Java 命令行工具 jhat、MAT)
? 运行时调用栈及其分析(Java 命令行工具 jstack);
? 高性能 I/O
? 基于设计模式的代码调优
? 代码重构
2实验环境配置
简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。
特别是要记录配置过程中遇到的问题和困难,以及如何解决的。
实验环境设置请参见 Lab-0 实验指南。 除此之外,本次实验需要你在 Eclipse IDE 中配置并运行 VisualVM 和 Memory Analyzer (MAT) ( 用 于 Java 程 序 动 态 性 能 分 析 的 工 具 )。 还要配置并运行 CheckStyle 和 SpotBugs 工具(代码静态分析工具)
配置VisualVM:
访 问
https://visualvm.github.io
1、下载中文版的visualVM 启动器 visualvm_142
2、 下载visualVM到Eclipse的插件:visualvm_launcher_u3_eclipse
3、 下载完成后将visualvm_142.zip 解压到软件安装目录作为一款单独软件运用配置。
4、 将visualvm_launcher_u3_eclipse.zip解压
5、 在Eclipse中加入新插件:help—>install new software
然后add—>local 刚刚解压的visualvm_launcher_u3_eclipse目录
6、安装成功后进行配置:
在window的preferences中进行VisualVM的配置,需要配置它的启动器(visualvm_142 的bin下的visualvm.exe执行文件)还有jdk目录。
7、配置完成visualVM之后,再进行启动注册的配置:,针对你的小程序进行选择启动器的配置
配置MAT
从Eclipse Marketplace中安装
配置CheckStyle
从Eclipse Marketplace中安装
配置spotbug
从Eclipse Marketplace中安装
3实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1Static Program Analysis
3.1.1人工代码走查(walkthrough)
列出你所发现的问题和所做的修改。每种类型的问题只需列出一个示例即可。
1.缩进过多或过少
解决:统一缩进4格
2.括 位置在函数下面
解决:括 放在函数后面
3.有多余空行
解决:删除多余空行
3.1.2使用CheckStyle和SpotBugs进行静态代码分析
列出你所发现的问题和所做的修改。每种类型的问题只需列出一个示例即可。
对比分析两种工具发现问题的能力和发现问题的类型上有何差异。
‘X’ 缩进了X个 checkStyle要求2个空格缩进
‘X’ 修饰符顺序weifan违反JLS建议 修饰符没有按照JLS建议排序(e.g. abstract public 应写成public abstract)
Javadoc 的第一句缺少一个结束时期 Javadoc 的第一行内容应该加上’.’
从switch块的前一个分支落入 查资料说是default分支不是最后一个分支,然而我这里是由于前一个case少了break。
‘X’ 子元素缩进了X个 checkStyle要求2个空格缩进
每一个变量的定义必须在它的声明处,且在同一行 要求同一行只声明一个变量。
变量’X 行)若需要存储该变量的值,请将其声明为final 从未改变值的变量应声明为final
数组大括 位置错误 String[ ] args 应改为 String args[ ]
导入语句’X’ 的字典顺序错误,应在X前 导入外部文件应按字典顺序
名称 ‘X’ 必须匹配表达式 ‘X’ 变量名要满足正则表达式 1([a-z0-9][a-zA-Z0-9]*)
差异:spotbug我借助它帮我找出我正在写的代码中的错误。与之前使用的 FindBugs 工具类似,SpotBugs 像一个严格的审计人员一样,其内部预编了数百个“bug 匹配模板”。它搜索你的代码,使用bug 模板进行匹配就可以帮助你找到代码中的错误。
Checkstyle(https://marketplace.eclipse.org/content/checkstyle-plug),在每个人都遵循相同规则并以同样的方式构建代码的项目工作会更加容易。大脑有一种学习风格的方式,当每个方法的空白处于同一个位置时,大脑就能更快地理解每个人的代码。当然,你可以编写 Checkstyle 插件来执行你自己所喜爱的规则,这意味着你可以自己动手定制自己独特的东西。
3.2Java I/O Optimization
3.2.1多种I/O实现方式
实现了哪些I/O方式来读写文件,具体如何实现的。
如何用strategy设计模式实现在多种I/O策略之间的切换。
1.stream
利用FileInputStream,FileOutputStream
这种方法主要是使用了以下类:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
这个方法是利用字节流来读取文件的内容,需要首先开一个字节数组,当文件比较大的时候,我们盲目的开的话会造成内存的空间损失。带来不必要的麻烦。我们可以申请读取进来的文件内容大小的字节数组。然后利用FileInputStream读入流获取字节,再利用new String(bytes, offset, length)构建出字符串对象即可,使用FileOutputStream类的write写回方法即可写回文件.
2.buffer
利用BufferedReader/Writer
这种方法主要是使用了以下类:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
为了提高写入的效率,使用了字符流的缓冲区。
创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联。
使用缓冲区中的方法将数据写入到缓冲区中。
使用缓冲区中的方法,将数据刷新到目的地文件中去。
对数据进行读入读出.
- Scanner/Printer
利用PrintWriter,Scanner
这种方法主要是使用了以下类:
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.util.Scanner;
public Scanner(InputStream source) 构造一个新的扫描器,它产生从指定的输入流扫描的值,从流字节转换为字符使用基础平台的默认字符集。从而从文件读取数据.
创建一个向指定文件的新的PrintWriter,创建一个中介输出流,创建一个向此输出流写入数据的新的PrintWriter,通过打印从toString产生的字符串来打印一个对象到文件中
strategy设计模式:新建一个io包,完成三个文件读写的.java文件,每个文件有一个度方法和一个写方法,每次读入数据或者写入数据到文本时,选择调用io包中的某一个类的读写方法来完成读写操作.
3.2.2多种I/O实现方式的效率对比分析
如何收集你的程序I/O语法文件的时间。
表格方式对比不同I/O的性能。
图形对比不同I/O的性能。
收集时间: long begin = System.currentTimeMillis();
调用某种读取文件策略(或者写入文件策略)
(stream和scannerprint返回的是string类型,buffer返回的是stringbuffer类型.)
long time=System.currentTimeMillis() – begin;
从而得到时间time
表格方式
StellarSystem.txt SocialNetworkCircle.txt
stream策略 读文件 64ms 71ms
写文件 104ms 197ms
buffer策略 读文件 921ms 971ms
写文件 359ms 313ms
Scanner/Printer策略 读文件 714ms 912ms
写文件 185ms 533ms
3.3Java Memory Management and Garbage Collection (GC)
3.3.1使用-verbose:gc参数
Java HotSpot? 64-Bit Server VM (25.192-b12) for windows-amd64 JRE (1.8.0_192-b12), built on Oct 6 2018 17:12:23 by “java_re” with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 8244076k(596456k free), swap 17913628k(2144476k free)
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:NewSize=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
26.333: [GC (System.gc()) [PSYoungGen: 6766K->1005K(9216K)] 6766K->2058K(19456K), 0.0150722 secs] [Times: user=0.03 sys=0.01, real=0.02 secs]
26.348: [Full GC (System.gc()) [PSYoungGen: 1005K->0K(9216K)] [ParOldGen: 1053K->1725K(10240K)] 2058K->1725K(19456K), [Metaspace: 5644K->5638K(1056768K)], 0.0663260 secs] [Times: user=0.09 sys=0.00, real=0.07 secs]
28.499: [GC (System.gc()) [PSYoungGen: 1609K->800K(9216K)] 3334K->2533K(19456K), 0.0053375 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
28.504: [Full GC (System.gc()) [PSYoungGen: 800K->0K(9216K)] [ParOldGen: 1733K->2021K(10240K)] 2533K->2021K(19456K), [Metaspace: 9840K->9840K(1058816K)], 0.0830394 secs] [Times: user=0.20 sys=0.00, real=0.08 secs]
32.746: [GC (System.gc()) [PSYoungGen: 1830K->160K(9216K)] 3851K->4546K(19456K), 0.0386513 secs] [Times: user=0.03 sys=0.00, real=0.04 secs]
32.785: [Full GC (System.gc()) [PSYoungGen: 160K->0K(9216K)] [ParOldGen: 4386K->3260K(10240K)] 4546K->3260K(19456K), [Metaspace: 10435K->10435K(1058816K)], 0.2095053 secs] [Times: user=0.25 sys=0.00, real=0.21 secs] Heap
PSYoungGen total 9216K, used 1801K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 21% used [0x00000000ff600000,0x00000000ff7c2620,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 2059K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 20% used [0x00000000fec00000,0x00000000fee02fb0,0x00000000ff600000)
Metaspace used 10543K, capacity 10744K, committed 10880K, reserved 1058816K
class space used 1267K, capacity 1327K, committed 1408K, reserved 1048576K
上面为得到的
32.746: [GC (System.gc()) [PSYoungGen: 1830K->160K(9216K)] 3851K->4546K(19456K), 0.0386513 secs] [Times: user=0.03 sys=0.00, real=0.04 secs]
32.785: [Full GC (System.gc()) [PSYoungGen: 160K->0K(9216K)] [ParOldGen: 4386K->3260K(10240K)] 4546K->3260K(19456K), [Metaspace: 10435K->10435K(1058816K)], 0.2095053 secs] [Times: user=0.25 sys=0.00, real=0.21 secs]
MinorGc箭头前后的数据1830k和160k分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有1830-160=1230的对象容量被回收,括 内的数据9216K为堆内存的总容量,收集所需要的时间是0.0386513秒(这个时间在每次执行的时候会有所不同),FullGC箭头前后的数据160和0分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有160 – 0=160的对象容量被回收,括 内的数据9216K为堆内存的总容量,收集所需要的时间是0.0386513秒,同理分析上面可知Full GC的速度比Minor GC慢的多。一个需要0.0386513secs另一个需要0.2095053secs,因此minor频率更高
3.3.2用jstat命令行工具的-gc和-gcutil参数
3.3.3使用jmap -heap命令行工具
运行出错,因此在同学电脑上运行的该命令
3.3.4使用jmap -clstats命令行工具
3.3.5使用jmap -permstat命令行工具
3.3.6使用JMC/JFR、jconsole或VisualVM工具
3.3.7分析垃圾回收过程
3.3.8配置JVM参数并发现优化的参数配置
3.4Dynamic Program Profiling
3.4.1使用JMC或VisualVM进行CPU Profiling
3.4.2使用VisualVM进行Memory profiling
3.5Memory Dump Analysis and Performance Optimization
3.5.1内存导出
3.5.2使用MAT分析内存导出文件
3.5.3发现热点/瓶颈并改进、改进前后的性能对比分析
3.5.4在MAT内使用OQL查询内存导出
3.5.5观察jstack/jcmd导出程序运行时的调用栈
3.5.6使用设计模式进行代码性能优化
3.6Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚本实验中要求的多个分支和master分支所指向的位置。
-
a-z ??
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!