软件构造-实验五-仅供参考
姓名 |
|
学号 |
|
班号 |
|
电子邮件 |
|
手机号码 |
目录
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 实验目标概述
2 本次实验通过对 Lab4 的代码进行静态和动态分析,发现代码中存在的不符 合代码规范的地方、具有潜在 bug 的地方、性能存在缺陷的地方(执行时间热点、 内存消耗大的语句、函数、类),进而使用第 4、7、8 章所学的知识对这些问题 加以改进,掌握代码持续优化的方法,让代码既“看起来很美”,又“运行起来 很美”。 具体训练的技术包括:
3 ⚫ 静态代码分析(CheckStyle 和 SpotBugs)
4 ⚫ 动态代码分析(Java 命令行工具 jstat、jmap、jcmd、VisualVM、JMC、 JConsole 等)
5 ⚫ JVM 内存管理与垃圾回收(GC)的优化配置
6 ⚫ 运行时内存导出(memory dump)及其分析(Java 命令行工具 jhat、MAT)
7 ⚫ 运行时调用栈及其分析(Java 命令行工具 jstack);
8 ⚫ 高性能 I/O
9 ⚫ 基于设计模式的代码调优
10 ⚫ 代码重构
11 实验环境配置
安装jdk 随意选择目录 只需把默认安装目录 \java 之前的目录修改即可
安装jre→更改→ \java 之前目录和安装 jdk 目录相同即可
安装完JDK后配置环境变量 计算机→属性→高级系统设置→高级→环境变量
系统变量→新建 JAVA_HOME 变量 。
变量值填写jdk的安装目录
系统变量→寻找 Path 变量→编辑
在变量值最后输入 %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
系统变量→新建 CLASSPATH 变量
变量值填写 .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar(注意最前面有一点)
系统变量配置完毕
12 实验过程
12.1 Static Program Analysis
12.1.1 人工代码走查(walkthrough)
我用的是甲骨文的代码规范。开始的地方需要有注释,
例如:
每一行的长度不要超过80个字符,例如:
超过的部分需要换行。
句子注释:
块注释:
变量的声明:
变量的初始化:
在类和方法之间需要有一行为空:
12.1.2 使用CheckStyle和SpotBugs进行静态代码分析
Checkstyle使用的是GOOGLE的代码规范。会在不符合规范的代码上面涂上黄色。
缺少摘要javadoc
修改后为
包名字需要全部都是小写字母
修改后为:
类名字需要以大写开头
修改后为
缺少句号
修改后
类中不能出现连续两个大写字母
修改后
12.2 Java I/O Optimization
12.2.1 多种I/O实现方式
我运用了bufferreader,stream以及bufferstream的方法来读文件,对于写文件我运用了filewriter来实现
这三种模式来实现。
对于strategy的切换模式
只用在strategy readbook = new 模式 此处切换就可以随意更改模式。
12.2.2 多种I/O实现方式的效率对比分析
只用在开头调用记录时间的starttime,方法调用结束处加上endtime,两个相减就是程序运行的时间。
|
|
StellarSystem.txt |
SocialNetworkCircle.txt |
Buffereader |
读文件 |
2383ms |
9756ms |
写文件 |
162ms |
3410ms |
|
stream |
读文件 |
1926ms |
7008ms |
写文件 |
109ms |
2166ms |
|
bufferstream |
读文件 |
1942ms |
6849ms |
写文件 |
99ms |
2225ms |
12.3 Java Memory Management and Garbage Collection (GC)
12.3.1 使用-verbose:gc参数
12.3.2 用jstat命令行工具的-gc和-gcutil参数
12.3.3 使用jmap -heap命令行工具
12.3.4 使用jmap -clstats命令行工具
12.3.5 使用jmap -permstat命令行工具
12.3.6 使用JMC/JFR、jconsole或VisualVM工具
Jconsole:
virtualVM:
12.3.7 分析垃圾回收过程
- Step 1: Marking 标记
第一步就是标记,也就是垃圾收集器会找出那些需要回收的对象所在的内存和不需要回收的对象所在的内存,并把它们标记出来,简单的说,也就是先找出垃圾在哪。
所有堆中的对象都会被扫描一遍,以此来确定回收的对象,所以这通常会是一个相对比较耗时的过程
- Step 2: Normal Deletion
垃圾收集器会清除掉上一步标记出来的那些需要回收的对象区域存在的问题就是碎片问题:
标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
- Step 2.1:
Deletion with Compacting 压缩
由于简单的清除可能会存在碎片的问题,所以又出现了压缩清除的方法,也就是先清除需要回收的对象,然后再对内存进行压缩操作,将内存分成可用和不可用两大部分
当开始回收垃圾的时候,所有的进程都会停止。
12.3.8 配置JVM参数并发现优化的参数配置
我选择调整这个程序的JVM参数。
在main方法上点击右键--选择 Run as--选择Run Configurations对运行程序进行配置。
在VM输入如下:
JVM配置如下:
-Xmx300m
-Xms300m
-Xmn100m
-XX:SurvivorRatio=8
-XX:+UseG1GC
-XX:MaxTenuringThreshold=14
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=8
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:/a.dump
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-XX:+TraceClassLoading
-XX:+PrintClassHistogram
12.4 Dynamic Program Profiling
12.4.1 使用JMC或VisualVM进行CPU Profiling
类的调用正常,进程正常。
12.4.2 使用VisualVM进行Memory profiling
堆的占用合理。
12.5 Memory Dump Analysis and Performance Optimization
12.5.1 内存导出
用visual VM导出堆内存报告
保存到本地,通过MAT分析。
12.5.2 使用MAT分析内存导出文件
可以看出程序并没有内存泄漏
Histogram
dominator tree
top consumers
12.5.3 发现热点/瓶颈并改进、改进前后的性能对比分析
改进后我新生成一份堆内存报告与上一份报告进行比较
占用少了很多,调用加快。优化成功。
12.5.4 在MAT内使用OQL查询内存导出
长度大于10的字符串:
大于特定大小的任意对象实例:
特定长度>100的字符串
Collection实例大于100的
12.5.5 观察jstack/jcmd导出程序运行时的调用栈
加上了thread.sleep进程:
12.5.6 使用设计模式进行代码性能优化
原本我使用一个内部类来描绘socialnetwork的关系网,但是在这个内部类内部我发现也只是初始化一些数组,于是我参照canonicalization 的思想,将内部类删除将其中的数组构造拉到外面。
12.6 Git仓库结构
13 实验进度记录
日期 |
时间段 |
计划任务 |
实际完成情况 |
5.27 |
18:30-23:00 |
写checkstyle |
写完 |
5.28 29 |
下午 |
使用设计模式优化 |
差不多完成 |
5.31 |
13:00-22:08 |
写剩余部分 |
完成 |
14 实验过程中遇到的困难与解决途径
遇到的难点 |
解决途径 |
不会startegy模式
|
通过百度明白需要几个部分,自己模仿 |
不会使用powershell运行程序
|
通过查询学会Java自带的命令。还知道需要绝对地址,相对地址不行 |
不会OQL查询
|
通过学习OQL语言的规则构造。 |
15 实验过程中收获的经验、教训、感想
15.1 实验过程中收获的经验和教训
15.2 针对以下方面的感受
(1) 代码“看起来很美”和“运行起来很美”,二者之间有何必然的联系或冲突?哪个比另一个更重要些吗?在有限的编程时间里,你更倾向于把精力放在哪个上?
(2) 诸如SpotBugs和CheckStyle这样的代码静态分析工具,会提示你的代码里有无数不符合规范或有潜在bug的地方,结合你在本次实验中的体会,你认为它们是否会真的帮助你改善代码质量?
(3) 为什么Java提供了这么多种I/O的实现方式?从Java自身的发展路线上看,这其实也体现了JDK自身代码的逐渐优化过程。你是否能够梳理清楚Java I/O的逐步优化和扩展的过程,并能够搞清楚每种I/O技术最适合的应用场景?
(4) JVM的内存管理机制,与你在《计算机系统》课程里所学的内存管理基本原理相比,有何差异?有何新意?你认为它是否足够好?
(5) JVM自动进行垃圾回收,从而避免了程序员手工进行垃圾回收的麻烦(例如在C++中)。你怎么看待这两种垃圾回收机制?你认为JVM目前所采用的这些垃圾回收机制还有改进的空间吗?
(6) 基于你在实验中的体会,你认为“通过配置JVM内存分配和GC参数来提高程序运行性能”是否有足够的回报?
(7) 通过Memory Dump进行程序性能的分析,JMC/JFR、VisualVM和MAT这几个工具提供了很强大的分析功能。你是否已经体验到了使用它们发现程序热点以进行程序性能优化的好处?
(8) 使用各种代码调优技术进行性能优化,考验的是程序员的细心,依赖的是程序员日积月累的编程中养成的“对性能的敏感程度”。你是否有足够的耐心,从每一条语句、每一个类做起,“积跬步,以至千里”,一点一点累积出整体性能的较大提升?
(9) 关于本实验的工作量、难度、deadline。
(10) 到目前为止,你对《软件构造》课程的意见与建议。
运行起来很美更重要,看起来很美只是次要的。我愿意把精力放在优化上。
SpotBugs和CheckStyle会提高我代码的可读性和程序的潜在bug,让我的程序更完美。
因为每一种IO都有应用的地方。肯定有用武之地。
配置JVM的内存分配对于每一个程序都不一样,提高堆的大小让我的程序读写次数降低。
对于分析程序的软件比如Memory Dump, JMC/JFR、VisualVM和MAT的确让程序内部不再迷茫。
软件构造课程让我学到了很多。