阿里java开发规范学习 汇总

阿里java开发规范:


本规范给出了范例,便于理解


结合自己在项目中的体会【】,谈一谈目前在项目中未能遵守的规范带来的麻烦和遵守了带来的方便之处。


编程规约
(一) 命名风格
3. 【强制】类名使用UpperCamelCase风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO


【这个是基本都知道的,但是面对一些专有名词,常常不知道应该是大写还是小写,写的时候偶尔XML ,Xml混写,导致最后维护代码最终统一造成麻烦】


例如:TCP or Tcp 
      XML or Xml
      DB  or Db
      WSDL or Wsdl
目前根据阿里指南,之后这种专用名词还是采用驼峰。
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion


8. 【强制】POJO类中布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误。 


【用eclipse生成get set方法的时候,由于返回值为boolean ,自动生成的方法名会再其前面加“is”,跟变量名+is会有冲突】


反例:定义为基本数据类型Boolean isDeleted;的属性,它的方法也是isDeleted(),RPC
框架在反向解析的时候,“以为”对应的属性名称是deleted,导致属性获取不到,进而抛出异常。


12. 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。 
刚开始接触java的时候,还不知道设计模式,根据以前写C的方式写,后来才知道是简单工厂模式。设计模式在其他语言经常见,例如单例模式,真的是经验总结出来的。


【将设计模式体现在名字中,有利于阅读者快速理解架构设计理念】
正例:public class OrderFactory; public class LoginProxy; public class ResourceObserver;


13. 【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的Javadoc注释。
尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。


【在代码评审中,由于入参出参 命名不准,加上没有javadoc的注释,完全看不懂服务接口的用途】


14. 接口和实现类的命名有两套规则: 
1)【强制】对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别。 正例:CacheServiceImpl实现CacheService接口。 
【这条项目组也要求强制满足,带来的好处特别明显,接手别人的代码,一来就能找到出入口和具体实现。
不仅这么命名,我们的接口都是xxx.xxxx.xxxx.api 对应的实现都在xxx.xxxx.xxxx.api.impl包下】


2) 【推荐】 如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able的形式)。 正例:AbstractTranslator实现 Translatable。
【这条经常用在定义接口的能力上,用able非常能够体现出使用】


16. 【参考】各层命名规约: 
A) Service/DAO层方法命名规约 
1) 获取单个对象的方法用get做前缀。 
2) 获取多个对象的方法用list做前缀。 
3) 获取统计值的方法用count做前缀。 
4) 插入的方法用save/insert做前缀。 
5) 删除的方法用remove/delete做前缀。 
6) 修改的方法用update做前缀。 
【现在所有的获取都取名为get ,对于多个对象的获取可以更详尽的命名】


B) 领域模型命名规约 
1) 数据对象:xxxDO,xxx即为数据表名。 
2) 数据传输对象:xxxDTO,xxx为业务领域相关的名称。 
3) 展示对象:xxxVO,xxx一般为网页名称。 
4) POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。
【之前项目组开会讨论,数据可和dubbo调用的类的命名,表示我们命名错了。。DO对应数据库表读取出来的对象,DTO对应dubbo 服务调用对象】


(二) 常量定义


3. 【推荐】不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。 说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在类CacheConsts下;系统配置相关常量放在类ConfigConsts下。
【目前工程对这方面不规范,喜欢将所有的常量放在一个路径下,并且将所有的参数注入也放在一个类之中,其实应该根据这些常量所属功能所在类进行划分】


4. 【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。 
1) 跨应用共享常量:放置在二方库中,通常是client.jar中的constant目录下。 
2) 应用内共享常量:放置在一方库中,通常是modules中的constant目录下。 反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示“是”的变量:
感受:现在工程没有采用两方库的方式,定义的api等函数是由客户端直接粘贴而来,而不是定义了公用接口包。
这样会带来两边的参数完全依赖客户端修改了的同学是否通知了调用端的同学同步他的代码,不同步会出现各类异常:
例如:找不到函数,找不到变量名而无法赋值,序列化的变量不正确。


【此外定义两方包的好处还在于同步两边对于某一String类型 int类型的常量值】
反例:类A中:public static final String YES = "yes"; 
类B中:public static final String YES = "y"; 
A.YES.equals(B.YES),预期是true,但实际返回为false,导致线上问题。


5. 【推荐】如果变量值仅在一个范围内变化,且带有名称之外的延伸属性,定义为枚举类。下面正例中的数字就是延伸信息,表示星期几。 
正例:public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}


【现有工程采用的比例子更好一点的定义,将1,2,3,4这种常量的含义定义在一个常量池,毕竟工程中3,4,5,6,7这种都会被检测为魔法值。
例子:
错误码按照系统分前缀
public enum ErrorCodePrefix {
    REGISTER(Constants.REGISTER_SERVICE_NO),//Constants.REGISTER_SERVICE_NO=01
    DBOPERATION(Constants.DB_SERVICE_NO);//Constants.DB_SERVICE_NO=02
}


枚举可以定义的更全面。现有工程使用的复杂枚举。
public enum ResponseEnum {
    SUCCESS(TransErrType.SUCCESS, "0000", "处理成功"),
    NOT_FOUND(TransErrType.FAILED, "0001", "没有找到对应信息"),
    NODE_NOT_EXIST(TransErrType.FAILED, "0001", "不存在");
    
    ResponseEnum(short errType, String errCode, String errDesc) {
        this.errType = errType;
        this.errCode = errCode;
        this.errDesc = errDesc;
    }
}】


(三) 代码格式
1. 【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果是非空代码块则: 
1) 左大括号前不换行。 
2) 左大括号后换行。 
3) 右大括号前换行。 
4) 右大括号后还有else等代码则不换行;表示终止的右大括号后必须换行。
【在阿里内部 换行括号党被征服了...目前大家都习惯左大括号不换行】


6. 【强制】注释的双斜线与注释内容之间有且仅有一个空格。
正例:// 注释内容,注意在//和注释内容之间有一个空格。
【一直在纠结1个空格好看还是不要空格好看还是2个空格好看,纠结的如同前面这句话一样混乱,之后可以不用纠结了】


7. 【强制】单行字符数限不超过 120 个,超出需要换行时 个,超出需要换行时 遵循如下原则: 
1) 第二行相对一缩进 4个空格,从第三行开始不再继续缩进参考示例。
 2) 运算符与下文一起换行。 
 3) 方法调用的点符号与下文一起换行。 
 4) 方法调用时,多个参数,需要换行时,在逗号后进行。 
 5) 在括号前不要换行,见反例。
 正例:
StringBuffer sb = new StringBuffer();
// 超过120个字符的情况下,换行缩进4个空格,点号和方法名称一起换行
sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
【现在遵守的120字符,在宽屏下基本能看完一行,而且基本满足了大多数情况不会超过120,如果超了120除了定义的常量名字或者类名
太长导致外,其他情况都可以做优化,例如函数传参太多等。调用x.xx().xx().xxx().xxx()太长明显可拆,而且应该拆等情况。
多个参数,逗号后换行
操作符换行
下一行缩进4个空格】


11. 【推荐】方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。 


【没有必要插入多个空行进行隔开。语音这个区分,根据每个人有不同的认为,但是好的分行可以看清思路和分块】


(四) OOP规约
4. 【强制】外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。
接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。


【过时的接口,暂时保留的应该加上@Deprecated注解,现有工程的接口描述 还可以更进一步(时间充裕可以优化)】


7. 【强制】所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。 说明:对于Integer var = ? 
在-128至127范围内的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这
个区间内的Integer值可以直接使用==进行判断,
但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。


【涨知识了 超过2的8次方的数,都用equals进行对象比较。】


8. 关于基本数据类型与包装数据类型的使用标准如下: 
1) 【强制】所有的POJO类属性必须使用包装数据类型。 
2) 【强制】RPC方法的返回值和参数必须使用包装数据类型。 
3) 【推荐】所有的局部变量使用基本数据类型。 说明:POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。 
正例:数据库的查询结果可能是null,因为自动拆箱,用基本数据类型接收有NPE风险。 
反例:比如显示成交总额涨跌情况,即正负x%,x为基本数据类型,调用的RPC服务,调用不成功时,返回的是默认值,页面显示为0%,
这是不合理的,应该显示成中划线。所以包装数据类型的null值,能够表示额外的信息,如:远程调用失败,异常退出。


【现有工程的传输的类属性都没有采用包装数据类型,从数据库中返回的信息也采用是基本类型,所以不会有NPE(NullPointerException),
但是带来的坏处就是入库和从数据库读取的数据由于采用默认的int类型等,本应该显示null的数据会默认成0,(目前工程要改动这个,改动太大,风险太高)】


9. 【强制】定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。 
反例:POJO类的gmtCreate默认值为new Date();
但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。


【不设定默认值,特别是数据库的DO,从而保证你控制每条数据的值进行入库前的确认,被调用方进行检测】


10. 【强制】序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值。
说明:注意serialVersionUID不一致会抛出序列化运行时异常。
【当时查这个问题很久,原来是客户端和服务端的序列化号不同,另外java虚拟机识别这个号进行反序列化时,会有环境问题也会导致反序列化失败】


11. 【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。
16. 【推荐】setter方法中,参数名称与类成员变量名称一致,this.成员名 = 参数名。在getter/setter方法中,不要增加业务逻辑,增加排查问题的难度。 反例:


【只是用this.xx构造赋值,最多是赋值的操作。参数校验等业务逻辑应该在set get 外再包一层,不应该放在构造器和set get方法内】


17. 【推荐】循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。 说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
【一直喜欢使用字符串的 拼接 + 符号 而不使用StringBuilder 的 append方法,现在明白了两者差异】


18. 【推荐】final可以声明类、成员变量、方法、以及本地变量,下列情况使用final关键字: 1) 不允许被继承的类,如:String类。 2) 不允许修改引用的域对象,如:POJO类的域变量。 3) 不允许被重写的方法,如:POJO类的setter方法。 4) 不允许运行过程中重新赋值的局部变量。 5) 避免上下文重复使用一个变量,使用final描述可以强制重新定义一个变量,方便更好地进行重构。
19. 【推荐】慎用Object的clone方法来拷贝对象。 说明:对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。


(五) 集合处理  【覆盖了大多数集合中常出现的错误和知识点,可以把这些规范当做知识点来学习】
1. 【强制】关于hashCode和equals的处理
【刚开始开会问的,必须要重写HashCode吗,当时看到规范以为必须遵守,实际上并不是这样,是在列表,hash表等集合处理,要用到查找,或者使分布更均匀,比较!!!的时候必须重写。


2. 【强制】 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException
3. 【强制】在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生ConcurrentModificationException 异常。
4. 【强制】使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
6. 【强制】泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用add方法,而<? super T>不能使用get方法,做为接口调用赋值时易出错。 
说明:扩展说一下PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>。


7. 【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
正例:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
} 反例:
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
【作为java常出现的问题,原因就是移除了一个元素后,所有元素的下标和集合的总长度会改变,但是采用迭代器的.nest是跨过上一个元素后remove掉上一个元素,不会受长度和元素下标的影响】


9. 【推荐】集合初始化时,指定集合初始值大小。 
说明:HashMap使用HashMap(int initialCapacity) 初始化, 
正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意 负载因子(即loader factor)默认为 0.75,如果 暂时无法 确定 初始值大小,请设置为 16(即默认值)。 
反例: HashMap需要 放置 1024个元素, 由于 没有设置容量 初始大小,随着元素不断增加容 量 7次被迫扩大, resize需要重建 hash表,严重影响性能。


【指定负载因子和初始化大小,避免哈希表频繁的扩容影响性能】


10. 【推荐】使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。
说明:keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。
而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。
正例:values()返回的是V值集合,是一个list集合对象;keySet()返回的是K值集合,是一个Set集合对象;entrySet()返回的是K-V值组合集合。


11. 【推荐】高度注意Map类集合K/V能不能存储null值的情况,如下表格:
集合类 Key Value Super 说明
Hashtable 不允许为null 不允许为null Dictionary 线程安全
HashMap 允许为null 允许为null AbstractMap 线程不安全
ConcurrentHashMap 不允许为null 不允许为null AbstractMap 锁分段技术(JDK8:CAS)
TreeMap 不允许为null 允许为null AbstractMap 线程不安全


反例: 由于HashMap的干扰,很多人认为ConcurrentHashMap是可以置入null值,而事实上,存储null值时会抛出NPE异常。


(六) 并发处理
5. 【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。 正例:注意线程安全,使用DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。


【采用JDK8 的应用,但是由于不熟悉,没有采用JDK8的时间类, 还采用的之前的Date Calendar去实现时间方面的运算。但是看了各方资料都推荐使用新的时间类,还有待去实践】


9. 【强制】多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
【平台组严禁使用了timer,推荐了ScheduledExecutorService 去实现定时任务,复杂的则使用quartz 框架去实现定时任务】


11. 【推荐】避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
说明:Random实例包括java.util.Random 的实例或者 Math.random()的方式。 正例:在JDK7之后,可以直接使用API ThreadLocalRandom,而在 JDK7之前,需要编码保证每个线程持有一个实例。
【写的模拟器采用的 Math.random 目前看来应该使用 ThreadLocalRandom去避免性能问题】


12. 【推荐】在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化问题隐患(可参考 The "Double-Checked Locking is Broken" Declaration),推荐解决方案中较为简单一种(适用于JDK5及以上版本),将目标属性声明为 volatile型。 反例:
class Singleton {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
// other methods and fields...
}


13. 【参考】volatile解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
如果是count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 
如果是JDK8,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。


(九) 其它
1. 【强制】在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。 
说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则);
【尝试从配置中传入正则表达式,那么可以采用读进来 在初始化完成正则表达式的编译 不在每次调用时去匹配 提高匹配速度】
例子: 
在spring 初始化时,不仅基础类型可以使用@value注解 方法也可以(考虑做)
@value
private initXXXXPattern(String xxxString) {
  if (check(xxxString)) {
  this.xxxPattern = Pattern.compile(xxxString);
  }
  LOG.warn("wrong")
}


5. 【强制】获取当前毫秒数System.currentTimeMillis(); 而不是new Date().getTime(); 说明:如果想获取更加精确的纳秒级时间值,使用System.nanoTime()的方式。
在JDK8中,针对统计时间等场景,推荐使用Instant类。
【需要加大对JDK8 中 时间类的了解,之前浅显的看过,针对时区,线程安全等的优化,但是如何灵活使用,还需要多练习】


8. 【推荐】及时清理不再使用的代码段或配置信息。
说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
正例:对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)来说明注释掉代码的理由。
【配置文件非常的冗长。。老的配置信息,例如编写时的过多的debug日志、断点代码、挡板返回标识等】


3. 【强制】对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。
对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。
【刚接项目活时,新手上路,大量的try catch 表现出对稳定性分析的不够,实际上只需要对真正有异常的地方捕获,对于每个地方都try catch 那说明很多地方没想清楚或惧怕不自信】


6. 【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。 说明:如果JDK7及以上,可以使用try-with-resources方式。
【之前回收资源还在 finally里面做,现在有resources,应该采用其进行资源回收,回收时的错,例如资源不存在根本没打开的异常捕获了可以打一条info帮助自己解析
(关闭流结果是打开时失败的异常,所以在finally中close 一定会报错)】


13. 【参考】避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。 说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。 正例:一个类中有多个public方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
private boolean checkParam(DTO dto) {...}
【能提的尽量提到公共包/各种方法中,复制粘贴带来的维护惨痛经历,前人告诉的太多了...自己也体会到了部分,虽然不惨痛,但是够痛苦】


7. 【推荐】谨慎地记录日志。生产环境禁止输出debug日志;有选择地输出info日志;如果使用warn来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志。 
说明:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。
【记录日志时请思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?】
【使用IDE时,拍错时,需要输出的信息 能够帮助排查的才打,多余的没用啊啊啊啊啊,或者没有日志怎么查】


(二) 索引规约
这一部分,作为知识点学习,在设计数据库时,没有过多参加。无法亲身做优化时,这些规则带来的优势。


六、工程结构
(一) 应用分层
3. 【参考】分层领域模型规约:
 DO(Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。
 DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
 BO(Business Object):业务对象。由Service层输出的封装业务逻辑的对象。
 AO(Application Object):应用对象。在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
 VO(View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
 Query:数据查询对象,各层接收上层的查询请求。注意超过2个参数的查询封装,禁止使用Map类来传输。、
【先用着】


9. 【推荐】二方库不要有配置项,最低限度不要再增加配置项。
10. 【参考】为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则:
 1)精简可控原则。移除一切不必要的API和依赖,只包含 Service API、必要的领域模型对象、Utils类、常量、枚举等。如果依赖其它二方库,尽量是provided引入,
 让二方库使用者去依赖具体版本号;无log具体实现,只依赖日志框架。 
 2)稳定可追溯原则。每个版本的变化应该被记录,二方库由谁维护,源码在哪里,都需要能方便查到。除非用户主动升级版本,否则公共二方库的行为不应该发生变化。
【项目的二方库 基础包 有日志框架,并且log的具体实现是放在配置项之中,维护简单了,但是改动起来很复杂】
例:
基类工程base包中定义了日志,及其日志配置项。
依赖的4个工程都不需要添加日志修改项,这样感觉是可行的。
但是如果base包中有个配置项,子工程有一个工程的配置要做此配置项做修改,修改不了。。。必须打开base的jar包重写配置项,增加了维护修改难度。


1. 【推荐】高并发服务器建议调小TCP协议的time_wait超时时间。 
说明:操作系统默认240秒后,才会关闭处于time_wait状态的连接,
在高并发访问下,服务器端会因为处于time_wait的连接数太多,可能无法建立新的连接,
所以需要在服务器上调小此等待值。 正例:在linux服务器上请通过变更/etc/sysctl.conf文件去修改该缺省值(秒): net.ipv4.tcp_fin_timeout = 30
【项目没设置该时间,dubbo貌似有自动关闭连接的能力,所以暂未考虑设置该值】


2. 【推荐】调大服务器所支持的最大文件句柄数(File Descriptor,简写为fd)。 
说明:主流操作系统的设计是将TCP/UDP连接采用与文件一样的方式去管理,即一个连接对应于一个fd。
主流的linux服务器默认所支持最大fd数量为1024,当并发连接数很大时很容易因为fd不足而出现“open too many files”错误,
导致新的连接无法建立。 建议将linux服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
【项目没调该参数,但是遇到了dubbo建立了1024+个线程后,该问题暴露】


3. 【推荐】给JVM设置-XX:+HeapDumpOnOutOfMemoryError参数,让JVM碰到OOM场景时输出dump信息。
4. 【推荐】在线上生产环境,JVM的Xms和Xmx设置一样大小的内存容量,避免在GC 后调整堆大小带来的压力。
说明:OOM的发生是有概率的,甚至有规律地相隔数月才出现一例,出现时的现场信息对查错非常有价值。
【崩溃时,输出堆栈信息,才能看到问题,不然一头雾水。刚开始服务都只设置了1G内存,频繁拼接字符串和new 对象的service层的MGC 非常 非常的频繁
,而且现在项目给的内存非常多 都是8G 16G上面部一个服务,那么不把JVM的内存大小改成4G以上 对得起这么大的内存吗】


上面是针对java手册中,提取的常常容易出错的部分。


望自己以后能够把其中高并发/集合部分能够有更深一层的理解和感悟。
还有JDK 8的新时间类型,localtime 和 instant 类的练习。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值