总结:工作 + 学习 (2019年)
1、关于对象结合JVM
写功能代码时,尽量在一个方法里少创建对象,因为并发量高的时候,频繁的创建和销毁大量对象,会对 JVM 产生巨大压力,可能会崩溃。
2、关于 JVM 参数设置
在实际开发中,可以工具测试一个对象占多少内存,计算下并发量假如1000的时候,对象一共会占用多少内存。再结合新生代、老年代的垃圾清除思想,来设置新
生代中三区、老年代的内存大小。
3、关于对象
在实际业务代码里,一般加了@Service @Controllere 的对象,都是创建一次,一直使用,所以会在创建后,经过 MinorGC 进入到老年代。
我们在接口,方法里面的局部变量,都是使用一次后基本就不再使用(除非方法逃逸),所以对象在新生代里,很快就会回收,不会进入老年代。
我们实际写业务代码,就尽量考虑到创建对象的时候,不要在一个方法里大量创建对象,避免频繁出发 MinorGC。(结合第一条心得)
4、关于常量类和数据字典
如果只是单纯的 key ,就可以用常量类 - 静态变量来定义;
如果是不可变 key + value 格式,可以用枚举来定义;
如果是不可变的 key 和可变的 value,那可变的 value 就从持久化的地方获取,例如 mysql。 key 的话就可以用常量类 - 静态变量来定义。
5、关于如何分析 JVM 优化
分析一个项目,1秒钟会产生多少对象,新声代的 Eden 区 和 Survivor 区有多大。多少秒后会发生一次 YGC。发生 YGC 时,如果有空间担保规则,老年代的剩余大小是否大于新生代每次回收存活对象平均内存大小。如果老年代放的下,则发生 YGC,如果老年代放不下,则发生 FullGC。
如果新生代的 Eden 区回收垃圾,存活的对象大于 Survivor 区的大小,则这些存活对象会直接存到老年代中,会使老年代迅速的存满对象。
所以优化,就是先分析上述情况。然后针对性的,调大堆大小以及 Survivor 区的大小,使对象从 Eden 区存活,会复制到 Survivor 区。
并且如果是大内存的机器,面向外网网友访问的,应该将垃圾回收器设置为 G1 垃圾回收器。
防止频繁 FullGC:禁止动态年龄判断,以及确保每次 MinorGC 后存活的对象,能放在 Survivor,不会直接移动到老年代。(让进入老年代的对象尽量少)
6、关于写代码
要按照规范来,让自己的代码具有规范性,可读性,简洁性,可用性。
7、关于 MySQL 建表
一个备用字段,几个字段能合并就尽量合并为一个。要考虑到后期扩展。字段名不用包含表名,例如rule表的名称直接name,不需要rule_name 。
8、关于 web 层代码
要对关键性参数做校验,例如非空检验。以及打印日志。可以在报错的时候,迅速定位到是哪里出现Bug。
9、关于写代码
尽量按照规范,或阿里规范或公司规范来进行命名,领域模型等来完成功能。
10、关于全局异常
先自定义一个异常,在代码逻辑里自己做的一些逻辑的失败处理就可以 return new 自定义异常。然后写一个全局异常,可以捕获自定义异常和 exception ,并且处理。(不用像之前笔记和小说项目,每个方法都 try catch)
11、关于打印日志
打印日志时,打印的类信息,有些字段有值,有些字段没有值,就的用 JsonObject 格式化。
JSONObject.toJSONString(arriveWarnRuleVO);
12、关于设计VO(字段太多,分属不同的类型)
设计VO时,如果涉及到多个类型,每个类型显示不同的详情,例如商品类型显示商品详情,订单类型显示订单详情。则可以每个类型的具体详情设计一个实体类,把该实体性的对象作为VO的字段。(对代码的可读性和整洁性的提高)
13、关于联表查询和分表查询
考虑到联表查询的IO次数少些,但是数据量大的时候,或者没有正确的索引,查询时间长些。
以及综合业务考虑,是否联表一次性查出来、一次性查询出来的数据量的多少、以及查询字段的多少、以及SQL是否太庞大。
我自己的习惯是,如果查询总数少,例如一次性只查1条或者20条,并且只能联表查询,那就联表查询。
如果业务不复杂,可以依次单表查询,那就单表查询。在代码里面组装数据。(代码的可读性,维护性更高。数据量不是很大的时候,联表和单表查询的耗时以及资源都相差不大)
14、关于Service 和 Dao 层的调用
除了本业务,例如 UserService 和 UserDao,service 直接调用 dao 层外,如 User 涉及 Product 表,则应该 UserService 调用 ProductService ,而不应调 ProductDao。
如果涉及到简单业务和复杂业务,都是一个类型功能,方法可以重载。例如在 ProductService 中可以将方法重载,不同的参数和不同的返回值。 (重载的思想)
15、当多线程、多人、多端口修改一条数据(乐观锁)
如何保证一致性:将修改之前的旧唯一值(修改必定会修改的字段 - 可以为时间、状态),将旧的值作为修改操作的条件,符合的时候才修改,不符合的话,update 操作会返回一个 int 值为0。
CAS操作。
返回值为0,代表未修改成
16、参数校验
用注解@validate(value={}) Object object,抛出MethodArgumentNotValidException异常(参数通不过校验),在全局异常里面捕获,处理返回提示信息。
value= 的表示分组,分组信息是在实体类的字段上注明,并且可以设置一个变量值。在一个地方统一返回信息,可以增加该变量值,可以做到返回信息的精细。