哈工大软件构造实验lab5

3.1.1 人工代码走查(walkthrough)

  1. 源文件名由其包含的顶级类(其中包含正好一个 )的区分大小写的名称,以及.java扩展名组成。及在里面建一个System的文件,再将各个包复制进去,而不是在src下直接写。
    2.多余空格字符,用自动格式化格式化
    3.文件编码:UTF-8,改变编码模式

4.导入语句,不能用*,例如import system.Exception*.应该为import system.Exceptions.StellarSystemException;
5. Java代码的列限制为100个字符。 除非如下所述,否则超过此限制的任何行都必须被换行,将超过字符限制的用回车移至下一行。
6.对于每个类和方法,要写注释,如/**

  • get planet.
  • @param i i
  • @return Planet
    */
    public Planet getPlanet(final int i) {
    Planet a = null;
    for (Planet one: this.getTrackobject().get(i)) {
    a = one;
    }
    return a;
    }
  1. 类和成员修饰符(如果存在)按照Java语言规范建议的顺序显示:

8.修饰符顺序
public protected private abstract default static final transient volatile synchronized native strictfp ,如改变 static private int max为 private static int max
9.包名称都是小写,如改变Track包为track
10,类名要以大写开头,如改变plant为Plant
11.参数名称要与原名称区别开来,例如this.distance = distance 改为 this.distance = newdistance.

3.1.2 使用CheckStyle和SpotBugs进行静态代码分析
列出你所发现的问题和所做的修改。每种类型的问题只需列出一个示例即可。
对比分析两种工具发现问题的能力和发现问题的类型上有何差异。

  1. 空格,缩进不符合要求,用ide自带的格式化功能可以直接解决这类问题
  2. 类名要大写,包名要小写,同时不能直接在src目录下,得在src目录下的子目录下,于是建system包,将其他一些类都移动进去,同时修改各个包名和类名,以符合要求。
  3. 包声明之后必须有空行,直接在之后换行即可
  4. 类的属性前必须有注释
    /**
  • max jiaodu.
    */
    private final int jiaodu = 360;
  1. 类的属性改为private,如
    private final List tracks = new ArrayList();
  2. 方法要写注释,若有异常抛出,也要有注释

    /**
  • readfile.
  • @param a file
  • @return 0
  • @throws Exception some Exception that file is nor regular.
    */
    public int readfile(final File a) throws Exception
  1. 每个包要有packge-info.java
    在包里建立packge-info.java
  2. 方法里的变量应该设置为final
    public int readfile(final File a)
  3. 方法里的变量不能与类的属性同名
    如public CentralUser(final String newName, final int newAge,
    final String newSex) {
    super(newName);
    this.age = newAge;
    this.sex = newSex;
    }
  4. 方法参数间要有空格,例子同上
  5. 方法里的魔术数字要去掉,如int temp = 1000;
    改为public static final int MAXNUMBR = 1000;
    int temp = MAXNUMBR;
    12.一行的自书不能太多,否则要换行。
    3.2 Java I/O Optimization
    3.2.1 多种I/O实现方式

实现了哪些I/O方式来读写文件,具体如何实现的。
使用了filewriter/filereader,iostream,ioscanner来实现
Filerread读进来的字用Arraylist存起来
Iostream读进来的每行按Arraylist存起来
Ioscanner读进来每行也用arraylist存起来
Filewriter直接将字符串数组的按每行写入文件
Iostream直接将字符串数组安每行写入文件
Ioscanner直接将字符串数组按每行写入文件
如何用strategy设计模式实现在多种I/O策略之间的切换。
public class Iomethond {
private Iostrategy straegy;
public Iomethond(Iostrategy strategy){
this.straegy = strategy;
}
public ArrayList read(File a) throws Exception
{
ArrayList list = this.straegy.read(a);
return list;
}
public void write(String name,ArrayList a) throws Exception {
this.straegy.write(name, a);

}

}
用接口设计了iostrategy,并在接口里定义read和write方法,在各自实现三种读写的方法,在写一个Iomethond方法,来通过接口来根据构造参数来调用不同读写方式实现类里的
Write和read即可。
3.2.2 多种I/O实现方式的效率对比分析

如何收集你的程序I/O语法文件的时间。
用junit测试来测试三种读写方式的时间,
long start=System.currentTimeMillis(); //获取开始时间 long end=System.currentTimeMillis(); //获取结束时间
用这两个函数来记录开始于结束时间,在这两个时间记录之间进行文件读写即可。
这是读行星系统大文件的结果

这是读社交网络大文件的结果

表格方式对比不同I/O的性能。
行星系统大文件 社交网络大文件
Fileread 读文件 320 283
写文件 313 671
Iostream 读文件 133 189
写文件 3049 3920
Iocsanner 读文件 1523 3952
写文件 272 520

图形对比不同I/O的性能。
读行星系统时间

读社交系统时间

3.3 Java Memory Management and Garbage Collection (GC)
3.3.1 使用-verbose:gc参数
在运行配置里输入
-verbose:gc
-Xloggc:d:/gc.log
-XX:SurvivorRatio=8
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]
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 [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 [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方括号内部的含义是GC前改内存区域已使用内存->GC后改内存区域使用容量(该内存区域总容量)箭头前后的数据1830k和160k分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有1830-160=1230的对象容量被回收,括号内的数据9216K为堆内存的总容量, 外部3851K->4546(19456)表示GC前java堆已使用容量->GC后java堆已使用容量(java堆总容量,收集所需要的时间是0.0386513秒(这个时间在每次执行的时候会有所不同),,FullGC方括号内部的含义是GC前改内存区域已使用内存->GC后改内存区域使用容量(该内存区域总容量)箭头前后的数据160和0分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有160 - 0=160的对象容量被回收,括号内的数据9216K为堆内存的总容量,外部3851K->4546(19456)表示GC前java堆已使用容量->GC后java堆已使用容量(java堆总容量,收集所需要的时间是0.0386513秒,同理分析上面可知Full GC的速度比Minor GC慢的多。一个需要0.0386513secs另一个需要0.2095053secs,minorGC频率更高
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 分析垃圾回收过程
新生代GC(Minor GC/Scavenge GC):发生在新生代的垃圾收集动作。因为Java对象大多都具有朝生夕灭的特性,因此Minor GC非常频繁(不一定等Eden区满了才触发),一般回收速度也比较快。在新生代中,每次垃圾收集时都会发现有大量对象死去,只有少量存活,因此可选用复制算法来完成收集。
老年代GC(Major GC/Full GC):发生在老年代的垃圾回收动作。Major GC,经常会伴随至少一次Minor GC。由于老年代中的对象生命周期比较长,因此Major GC并不频繁,一般都是等待老年代满了后才进行Full GC,而且其速度一般会比Minor GC慢10倍以上。另外,如果分配了Direct Memory,在老年代中进行Full GC时,会顺便清理掉Direct Memory中的废弃对象。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记—清除算法或标记—整理算法来进行回收。
新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从Eden到Survivor,最后到老年代。
用Java VisualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,然后清空继续装载,当老年代也满了后,就会报outofmemory的异常

3.3.8 配置JVM参数并发现优化的参数配置
通过设置我们希望达到一些目标:
• GC的时间足够的小
• GC的次数足够的少
• 发生Full GC的周期足够的长
前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。
(1)针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值
(2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
(3)年轻代和年老代设置多大才算合理?这个我问题毫无疑问是没有答案的,否则也就不会有调优。我们观察一下二者大小变化有哪些影响
• 更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
• 更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率
• 如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 (B)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间
(4)在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC ,默认为Serial收集
(5)线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
最后调优的参数:
-Xms512M
-Xmx512M
-Xmn164m
-XX:PermSize=96m
-XX:MaxPermSize=96m
-XX:+UseParallelGC
-XX:CMSInitiatingOccupancyFraction=85

3.4 Dynamic Program Profiling
3.4.1 使用JMC或VisualVM进行CPU Profiling

3.4.2 使用VisualVM进行Memory profiling

3.5 Memory Dump Analysis and Performance Optimization
3.5.1 内存导出
使用jps找到当前进程的号码,再将其用控制台导出

3.5.2 使用MAT分析内存导出文件

3.5.3 发现热点/瓶颈并改进、改进前后的性能对比分析
①改进前,读文档遇到边的端点的标签,要把相应的顶点加入边的list属性是通过遍历一遍所有的顶点,匹配相应的顶点,加入边的list中,这样太耗费时间,每次向边中加入端点耗费的时间都是O(n)的操作。改进策略:在图中加入一个map属性,map的key是顶点的label,map的value是顶点对象。在读文档遇到边的端点的label时,可以直接根据map中的映射利用label得到结点,花费时间是O(1)的,大大节省了时间。
②在向图中加入边时,需要判断边的顶点是否在图中已经存在,之前采用的方法是遍历一遍所有的顶点看边的顶点是否已经存在,消耗是时间是O(n)的。改进方法:将顶点提前加入一个set中,要判断的时候直接调用set.contains()方法,因为set是存储特点,所以查找消耗时间的复杂度是O(1),大大节省了时间。
③在对图中的list操作时,做了防御性拷贝/深拷贝,导致消耗了90%的时间,由于在整个过程中不会对ADT中的属性做违法修改,所以直接返回部分可变属性,节省了90%的时间。

3.5.4 在MAT内使用OQL查询内存导出

3.5.5 观察jstack/jcmd导出程序运行时的调用栈

3.5.6 使用设计模式进行代码性能优化
Git仓库结构请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚本实验中要求的多个分支和master分支所指向的位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值