默认分类

默认分类

默认分类描述

Java同学,日常翻看的中文技术网站

admin 发表了文章 • 0 个评论 • 171 次浏览 • 2017-01-17 10:43 • 来自相关话题

你还在学习吗?

1.内容生产者
InfoQ
http://www.infoq.com/cn/
中文技术第一站,佩服霍老板,真金白银地为中国程序员们生产内容。

ImportNew
http://www.importnew.com
专门面向Java的内容生产者兼聚合者,但也有不少凑数的小白文。

并发编程网
http://ifeve.com
面向高并发,Java,开源产品的社区。

2. 内容聚合者
现在写博客,没人用RSS读了,在微博里的传播也就一两天,好在有它们负责挖掘整理散落在各个角落的技术文章。

开发者头条
http://www.toutiao.io 

码农周刊
http://weekly.manong.io/issues/
人气慢慢上来了,我博客阅读量很大一部分源于它,App也方便。

编程狂人周刊 
http://www.tuicool.com/mags
by 推酷,另一份周刊。

3. 还坚持博客为主的同学

左耳朵耗子,左大神的酷壳
http://coolshell.cn

鸟窝,在后端开发上,很多相同的关注
http://colobu.com

占小狼,Java与Netty
http://www.jianshu.com/users/90ab66c248e6

Java译站,淘宝有孚,性能文章
http://it.deepinmind.com

泥瓦匠,非常勤奋的同学
http://www.bysocket.com

ThoughtWorks洞见,TW的同学
http://insights.thoughtworkers.org 

花钱的年华,自家博客
http://calvin1978.blogcn.com

4. 公众号
大家现在都习惯用手机了,尽管公众号有不方便搜索,文章后续更新,不給放链接等种种不好。
前面各个媒体的公众号,还有像池老师的MacTalk,西乔女神的神秘的程序员们,朱女神的嘀嗒嘀嗒,小道消息这些著名公号,就不啰嗦了。

1. 高可用架构,Tim创立的高可用架构社区,线上线下分享的架构文章的整理。
2. 中生代技术,新晋的中生代技术社区,线上线下分享文章的整理。
3. 聊聊架构,InfoQ的架构方面精选文章。
4. 你假笨,by 寒泉子,阿里JVM团队。由JVM开发者亲自来说的种种细节,比我这种连蒙带猜的强到不知哪里去了。
5. HelloJava,by 毕玄,bluedavy,阿里大神,Java问题排查的各种Case,各种知识点。
6. 开涛的博客,by 开涛,京东
7. 美团点评技术团队,by 美团点评
8. 架构师之路,by 沈剑, 58
9. 春天的旁边,by 江南白衣,唯品会
10. 写程序的康德
11. 瞬息之间,by mindwind,京东
12. 开发资讯 ,by  臧秀涛,InfoQ,低调无比的公号名。
  查看全部
你还在学习吗?

1.内容生产者
InfoQ
http://www.infoq.com/cn/
中文技术第一站,佩服霍老板,真金白银地为中国程序员们生产内容。

ImportNew
http://www.importnew.com
专门面向Java的内容生产者兼聚合者,但也有不少凑数的小白文。

并发编程网
http://ifeve.com
面向高并发,Java,开源产品的社区。

2. 内容聚合者
现在写博客,没人用RSS读了,在微博里的传播也就一两天,好在有它们负责挖掘整理散落在各个角落的技术文章。

开发者头条
http://www.toutiao.io 

码农周刊
http://weekly.manong.io/issues/
人气慢慢上来了,我博客阅读量很大一部分源于它,App也方便。

编程狂人周刊 
http://www.tuicool.com/mags
by 推酷,另一份周刊。

3. 还坚持博客为主的同学

左耳朵耗子,左大神的酷壳
http://coolshell.cn

鸟窝,在后端开发上,很多相同的关注
http://colobu.com

占小狼,Java与Netty
http://www.jianshu.com/users/90ab66c248e6

Java译站,淘宝有孚,性能文章
http://it.deepinmind.com

泥瓦匠,非常勤奋的同学
http://www.bysocket.com

ThoughtWorks洞见,TW的同学
http://insights.thoughtworkers.org 

花钱的年华,自家博客
http://calvin1978.blogcn.com

4. 公众号
大家现在都习惯用手机了,尽管公众号有不方便搜索,文章后续更新,不給放链接等种种不好。
前面各个媒体的公众号,还有像池老师的MacTalk,西乔女神的神秘的程序员们,朱女神的嘀嗒嘀嗒,小道消息这些著名公号,就不啰嗦了。

1. 高可用架构,Tim创立的高可用架构社区,线上线下分享的架构文章的整理。
2. 中生代技术,新晋的中生代技术社区,线上线下分享文章的整理。
3. 聊聊架构,InfoQ的架构方面精选文章。
4. 你假笨,by 寒泉子,阿里JVM团队。由JVM开发者亲自来说的种种细节,比我这种连蒙带猜的强到不知哪里去了。
5. HelloJava,by 毕玄,bluedavy,阿里大神,Java问题排查的各种Case,各种知识点。
6. 开涛的博客,by 开涛,京东
7. 美团点评技术团队,by 美团点评
8. 架构师之路,by 沈剑, 58
9. 春天的旁边,by 江南白衣,唯品会
10. 写程序的康德
11. 瞬息之间,by mindwind,京东
12. 开发资讯 ,by  臧秀涛,InfoQ,低调无比的公号名。
 

我的Java后端书架 (2016年暖冬版)

admin 发表了文章 • 0 个评论 • 138 次浏览 • 2017-01-17 10:40 • 来自相关话题

本书架主要针对Java后端开发和架构师。
更偏爱那些能用简短流畅的话,把少壮不努力的程序员所需的基础补回来的薄书,而有些教课书可能很著名,但干涩枯燥,喋喋不休的把你带回到大学课堂上昏昏欲睡,不录。

1. 操作系统与网络的书
《Linux内核设计与实现 第3版》
Robert Love用最薄的篇幅,顺畅的文字将Linux内核主要的算法讲清楚了,《深入理解Linux内核》,《深入Linux内核架构》之类厚厚的全是代码,不是专门的内核程序员看这本足够了。
《Linux系统编程 第2版》
继续是Robert Love,比起APUE也是以薄见长,专门针对重要的系统调用讲解。
《性能之巅》
操作系统的性能调优、监控、工具和方法论,看这本就够了,已经足够厚,可能是书单里最厚的一本,但值得作为一本枕头书。

《TCP/IP详解 卷1:协议》
这么多年过去了,TCP的书好像主要还是只有这一本,有点旧了,看了也还是半懂不懂的。后人写的第二版也出来

《WireShark网络分析就这么简单》和 《WireShark网络分析的艺术》
多少人,是看了这两本轻松又实战的书,才真正理解TCP的细节。

PS:《UNIX环境高级编程》和《UNIX网络编程》,APUE和UNP更多作为一本超厚工具书存在。《Unix 编程艺术》,扯的都是闲篇,厚厚的一本其实略读一下就行。 《现代操作系统 第3版》如果看LKD未尽兴,可以回头看看这本基础概念,感觉比那本枯燥的《操作系统概念》(恐龙书)读起来舒服。

《TCP/IP指南》 前面wireshark书作者的推荐,网上有英文免费版,然后有中文版的卷1和卷2,但可能那么多章节那么厚你只关心TCP和HTTP两部分。

《HTTP权威指南》,同样是自己从厚厚的目录里挑选感兴趣的章节来看。

另外,那些日本韩国人写的《图解XXX》感觉都不喜欢。

2. 算法的书
《数据结构与算法分析-Java语言描述 第3版》
够薄,数据结构与算法分析的点基本都涵盖了,而且喜欢它的示例代码是Java写的,新出了第3版。
《算法 第4版》
可与上一本对比着读,厚一些,也多些图,但知识点没上面的全,也是Java的。
PS: 《数学之美》、《编程珠玑》,都是专栏文章,讲得并不系统,可以当兴趣读物来看。
数学系偏爱无比枯燥很多公式的《算法导论》,计算机系喜欢实用主义的《算法设计与分析基础 第3版》。

3. 架构设计的书
《软件系统架构:使用视点和视角与利益相关者合作 第2版》
也是教科书,最难得的是,这本老书在十年后的去年升级了第二版,所以感觉鲜活了好多,也许是最鲜活的一本架构书。
《恰如其分的软件架构 - 风险驱动的设计方法》
由于人类与生俱来的惰性,计算机原本科学的、精准的设计方式,有了敏捷的借口之后就很难再维持了。本书就是在这种背景下,提出由风险来决定设计的度。除了开始的风险驱动部分,其余部分就是规规矩矩标标准准的架构师教科书。
《SRE:Google运维解密》
广告词是 “地球上有这么一个团队,将运维推向极限高度”,的确很多值得翻的东西。
《发布!软件的设计与部署 》
关于高可靠性的软件,学校里不会教,出来社会却要面对的那部分,英文的原标题更清晰。
《大型网站技术架构:核心原理与案例分析》
淘宝出品,大型互联网站的科普入门书。
《高扩展性网站的50条原则》 
同是入门级读物。如果还有个高可用50条原则,那就齐了。
《微服务设计》 
那么多微服务的书,还是这本比较不像赚快钱的。
《大数据日知录》
前几年参加各种技术会议,CAP,最终一致性,RWN,向量时钟,Paxos,一致性哈希,Gossip什么的能灌你一耳朵。而现在,你只要在家安安静静的看书就够了。不过这个领域发展太快,又一年过去了,期望它可以持续出新版。

PS: 关于设计模式,我以前曾经有过很多很多本,GOF23啦,企业应用架构模式啦,EIP啦, POSA 5卷本啦,反模式啦,JavaEE/SOA/Restful的模式啦。但现在觉得对新人来说,一本Java写的《Head First 设计模式》,知道什么叫设计模式就够了。
《程序员必读之软件架构》作者维护着http://codingthearchitecture.com 。不过中文书名叫“必读”有点过。

4. Java语言的书
《Java并发编程实战》
Java并发经典,人手一本不用多说了。
《实战Java高并发程序设计》 
国人新作,流畅易读,内容也比上面一本来得新。
《Java8 实战》
Java8的新特性讲得最全最仔细的。
《深入理解 Java 虚拟机 第2版》
理解虚拟机并不是那么难,Java程序员来说,很多知识其实是必须的。另外还有几本类似主题的书,忽然一下子都出来了。
《Java性能权威指南》
比起多年前那部调优圣经,讲得更加深入,也更加贴近现在的JDK。可以从里面挑些知识点来,做Java调优的面试题。
《有效的单元测试》
不同于那些动辄BDD的高深书籍,专注于如何写“好”的,可维护的单元测试,拿来给团队看,能省很多口水。

PS:《Effective Java》外界一致推崇,但有点太过誉了。另外《Thinking in Java》有点太旧了,而且作者思路随意,译者语言晦涩,新程序员还是建议同时再看两卷《Java核心技术 - Core Java》
5. 程序员的自我修养
PS. 最近没买什么新书,随便说点旧书:
《程序员修炼之道-从小工到专家》,Pragmatic Programmer--注重实效的程序员开山之作,翻译的马达维文笔也和熊节一样好。
《代码整洁之道》和 《程序员的职业素养》,英文名是很相近的《Clean Code》和 《Clean Coder》,应该接替《代码大全2》成为必看的系列,因为后者太厚了,而且也有不少过时的东西,要自己去过滤。
《重构》很厚,但最有价值就是前面几章的洗脑篇,具体实作不如薄薄的《重构手册》。
关于敏捷的书,最开始的那本《解析极限编程--拥抱变化》就很好,再随便找本Scrum的流程看看就够了,《敏捷开发的艺术》也不错。
《布道之道》,经常在组织里推行新技术的同学可以看下,七种怀疑论者模式,脑海中一幅幅熟悉的面孔。
PS. 温伯格的书网上很推崇,《成为技术领导者》之类的,但我觉得年代太远,读起来其实没多大意思,一两个鸡汤观点还要自己从书里慢慢淘,有那功夫不如看点别的。
最后,没有包含同样很重要的数据库与Docker的书,不熟不推。 查看全部
本书架主要针对Java后端开发和架构师。
更偏爱那些能用简短流畅的话,把少壮不努力的程序员所需的基础补回来的薄书,而有些教课书可能很著名,但干涩枯燥,喋喋不休的把你带回到大学课堂上昏昏欲睡,不录。

1. 操作系统与网络的书
《Linux内核设计与实现 第3版》
Robert Love用最薄的篇幅,顺畅的文字将Linux内核主要的算法讲清楚了,《深入理解Linux内核》,《深入Linux内核架构》之类厚厚的全是代码,不是专门的内核程序员看这本足够了。
《Linux系统编程 第2版》
继续是Robert Love,比起APUE也是以薄见长,专门针对重要的系统调用讲解。
《性能之巅》
操作系统的性能调优、监控、工具和方法论,看这本就够了,已经足够厚,可能是书单里最厚的一本,但值得作为一本枕头书。

《TCP/IP详解 卷1:协议》
这么多年过去了,TCP的书好像主要还是只有这一本,有点旧了,看了也还是半懂不懂的。后人写的第二版也出来

《WireShark网络分析就这么简单》和 《WireShark网络分析的艺术》
多少人,是看了这两本轻松又实战的书,才真正理解TCP的细节。

PS:《UNIX环境高级编程》和《UNIX网络编程》,APUE和UNP更多作为一本超厚工具书存在。《Unix 编程艺术》,扯的都是闲篇,厚厚的一本其实略读一下就行。 《现代操作系统 第3版》如果看LKD未尽兴,可以回头看看这本基础概念,感觉比那本枯燥的《操作系统概念》(恐龙书)读起来舒服。

《TCP/IP指南》 前面wireshark书作者的推荐,网上有英文免费版,然后有中文版的卷1和卷2,但可能那么多章节那么厚你只关心TCP和HTTP两部分。

《HTTP权威指南》,同样是自己从厚厚的目录里挑选感兴趣的章节来看。

另外,那些日本韩国人写的《图解XXX》感觉都不喜欢。

2. 算法的书
《数据结构与算法分析-Java语言描述 第3版》
够薄,数据结构与算法分析的点基本都涵盖了,而且喜欢它的示例代码是Java写的,新出了第3版。
《算法 第4版》
可与上一本对比着读,厚一些,也多些图,但知识点没上面的全,也是Java的。
PS: 《数学之美》、《编程珠玑》,都是专栏文章,讲得并不系统,可以当兴趣读物来看。
数学系偏爱无比枯燥很多公式的《算法导论》,计算机系喜欢实用主义的《算法设计与分析基础 第3版》。

3. 架构设计的书
《软件系统架构:使用视点和视角与利益相关者合作 第2版》
也是教科书,最难得的是,这本老书在十年后的去年升级了第二版,所以感觉鲜活了好多,也许是最鲜活的一本架构书。
《恰如其分的软件架构 - 风险驱动的设计方法》
由于人类与生俱来的惰性,计算机原本科学的、精准的设计方式,有了敏捷的借口之后就很难再维持了。本书就是在这种背景下,提出由风险来决定设计的度。除了开始的风险驱动部分,其余部分就是规规矩矩标标准准的架构师教科书。
《SRE:Google运维解密》
广告词是 “地球上有这么一个团队,将运维推向极限高度”,的确很多值得翻的东西。
《发布!软件的设计与部署 》
关于高可靠性的软件,学校里不会教,出来社会却要面对的那部分,英文的原标题更清晰。
《大型网站技术架构:核心原理与案例分析》
淘宝出品,大型互联网站的科普入门书。
《高扩展性网站的50条原则》 
同是入门级读物。如果还有个高可用50条原则,那就齐了。
《微服务设计》 
那么多微服务的书,还是这本比较不像赚快钱的。
《大数据日知录》
前几年参加各种技术会议,CAP,最终一致性,RWN,向量时钟,Paxos,一致性哈希,Gossip什么的能灌你一耳朵。而现在,你只要在家安安静静的看书就够了。不过这个领域发展太快,又一年过去了,期望它可以持续出新版。

PS: 关于设计模式,我以前曾经有过很多很多本,GOF23啦,企业应用架构模式啦,EIP啦, POSA 5卷本啦,反模式啦,JavaEE/SOA/Restful的模式啦。但现在觉得对新人来说,一本Java写的《Head First 设计模式》,知道什么叫设计模式就够了。
《程序员必读之软件架构》作者维护着http://codingthearchitecture.com 。不过中文书名叫“必读”有点过。

4. Java语言的书
《Java并发编程实战》
Java并发经典,人手一本不用多说了。
《实战Java高并发程序设计》 
国人新作,流畅易读,内容也比上面一本来得新。
《Java8 实战》
Java8的新特性讲得最全最仔细的。
《深入理解 Java 虚拟机 第2版》
理解虚拟机并不是那么难,Java程序员来说,很多知识其实是必须的。另外还有几本类似主题的书,忽然一下子都出来了。
《Java性能权威指南》
比起多年前那部调优圣经,讲得更加深入,也更加贴近现在的JDK。可以从里面挑些知识点来,做Java调优的面试题。
《有效的单元测试》
不同于那些动辄BDD的高深书籍,专注于如何写“好”的,可维护的单元测试,拿来给团队看,能省很多口水。

PS:《Effective Java》外界一致推崇,但有点太过誉了。另外《Thinking in Java》有点太旧了,而且作者思路随意,译者语言晦涩,新程序员还是建议同时再看两卷《Java核心技术 - Core Java》
5. 程序员的自我修养
PS. 最近没买什么新书,随便说点旧书:
《程序员修炼之道-从小工到专家》,Pragmatic Programmer--注重实效的程序员开山之作,翻译的马达维文笔也和熊节一样好。
《代码整洁之道》和 《程序员的职业素养》,英文名是很相近的《Clean Code》和 《Clean Coder》,应该接替《代码大全2》成为必看的系列,因为后者太厚了,而且也有不少过时的东西,要自己去过滤。
《重构》很厚,但最有价值就是前面几章的洗脑篇,具体实作不如薄薄的《重构手册》。
关于敏捷的书,最开始的那本《解析极限编程--拥抱变化》就很好,再随便找本Scrum的流程看看就够了,《敏捷开发的艺术》也不错。
《布道之道》,经常在组织里推行新技术的同学可以看下,七种怀疑论者模式,脑海中一幅幅熟悉的面孔。
PS. 温伯格的书网上很推崇,《成为技术领导者》之类的,但我觉得年代太远,读起来其实没多大意思,一两个鸡汤观点还要自己从书里慢慢淘,有那功夫不如看点别的。
最后,没有包含同样很重要的数据库与Docker的书,不熟不推。

Java神器BTrace,从入门到熟练小工的手册

admin 发表了文章 • 0 个评论 • 198 次浏览 • 2017-01-17 10:35 • 来自相关话题

BTrace是神器,每一个需要每天解决线上问题,但完全不用BTrace的Java工程师,都是可疑的 -- 凯尔文. 萧
BTrace的最大好处,是可以通过自己编写的脚本,获取应用的一切调用信息。而不需要不断地修改代码,加入System.out.println(), 然后重启,然后重启,然后重启应用!!!
同时,特别严格的约束,保证自己的消耗特别小,只要定义脚本时不作大死,直接在生产环境打开也没影响。
在网上搜索BTrace出来的文章都有点旧了,而且不够详细,于是决定,重新写一份,这个版本顺便把代码改成可C&P的。

1. 概述
1.1 快速开始
BTrace搬家了!! 已经从 Sun 搬到了http://github.com/btraceio/btrace,当前版本已是1.38。
在Release页面里下载最新Zip版,解压就能用,UserGuide和Samples也在里面。
先抄一个UserGuide里的例子:import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class HelloWorld {
@OnMethod(clazz="java.lang.Thread", method="start")
public static void onThreadStart() {
println("thread start!");
}
}然后ps找出要监控的java应用的pid, ./btrace $pid HelloWorld.java 就跑起来了。
是不是很简单??基本上不用任何BTrace的知识,都能猜出HelloWorld会干啥。通过JVM Attach API,BTrace把自己绑进了被监控的进程,按HelloWorld.java里的定义,进行AOP式的代码植入。
最开心就是这里,如果还想监控其他内容,直接修改HelloWorld.java,再执行一次btrace就可以了,不需要重启应用!! 重启应用!!
1.2 典型的场景
1. 服务慢,能找出慢在哪一步,哪个函数里么?
2. 谁调用了System.gc(),调用栈为何?
3. 谁构造了一个超大的ArrayList?
4. 什么样的入参或对象属性,导致抛出了这个异常?或进入了这个处理分支?
1.3 一些重要事情
为了避免BTrace脚本的消耗过大影响真正业务,所以定义了一系列不允许的事情:比如只能调用BTraceUtils 里的一系列方法和脚本里定义的static方法,不允许其他调用任何类的任何方法。 比如不允许创建对象,比如不允许For 循环等等,更多规定看User Guide。 
当然,可以用-u 运行在unsafe mode来规避限制,但不推荐。
在以前的例子里,甚至还不能字符串相加,必须用strcat:
println(strcat(strcat(probeClass, "."), probeMethod));
好在新版里已经可以写回:
println(probeClass + '.' + probeMethod);
另外,BTrace植入过的代码,会一直在,直到应用重启为止。所以即使BTrace退出了,业务函数每次执行时都会多出一次BTrace是否Attach状态的判断。
最后,记得用Eclipse,而不是写字板来写脚本。

1.4 其他命令行选项
1.4.1 定义classpath
如果在HelloWorld.java里使用了JDK外的其他类,比如Netty的:
./btrace -cp .:netty-all-4.0.41.Final.jar $pid HelloWorld.java
但上面定义的classpath只在编译脚本时使用,而脚本里需要显式使用非JDK类的机会其实很少(后面真正用到的时候会提起)。
而在运行时,因为已经绑到目标应用的JVM里,用的是目标JVM的classpath。 
1.4.2 结果输出到文件
./btrace -o mylog $pid HelloWorld.java
很坑新人的参数,首先,这个mylog会生成在应用的启动目录,而不是btrace的启动目录。其次,执行过一次-o之后,再执行btrace不加-o 也不会再输出回console,直到应用重启为止。
所以最好直接用转向了事:
./btrace $pid HelloWorld.java > mylog
1.4.3.预编译脚本
虽然btrace可以实时编译Java源文件,但如果你的脚本是要给运维同学执行的,线上运行时才发现写错了就尴尬了。此时可以用btracec预编译一下:
./btracec HelloWorld.java
接下来,开始一步步讲解脚本的编写。 

2. 拦截方法定义

2.1 精准定位
就是HelloWorld的例子,精确定义要监控的类名与方法名。
 
2.2 正则表达式定位
批量定义需要监控的类与方法。正则表达式需要写在两个 "/" 中间。
下例监控javax.swing下的所有类的所有方法....会非常慢,实际使用时范围还是要窄些。@OnMethod(clazz="/javax\\.swing\\..*/", method="/.*/")
public static void swingMethods( @ProbeClassName String probeClass, @ProbeMethodName String probeMethod) {
print("entered " + probeClass + "." + probeMethod);
}通过在拦截函数的参数定义里注入@ProbeClassName和 @ProbeMethodName参数,告诉脚本实际匹配到的类和方法名。
另一个例子,监控Statement的executeUpdate(), executeQuery() 和 executeBatch() 三个方法:method = "/execute($|Update|Query|Batch)/"

2.3 按接口,父类,Annotation定位
要匹配所有Filter类,在接口或父类的名称前面,加个 "+" 就行
@OnMethod(clazz="+com.vip.demo.Filter", method="doFilter")
也可以按类或方法上的annotaiton匹配,前面加个@就行
@OnMethod(clazz="@javax.jws.WebService", method="@javax.jws.WebMethod")

2.4 其他
1. 构造函数的名字是 "<init>"
@OnMethod(clazz="java.net.ServerSocket", method="<init>")

2. 静态内部类的写法,是在类与内部类之间加上"$"
@OnMethod(clazz="co m.vip.MyServer$MyInnerClass", method="run")
3.如果有多个同名的函数,想区分开来,可以在拦截函数上定义不同的参数列表(见4.1)。
3. 拦截时机
可以为同一个函数的不同Location,分别定义多个拦截函数。
3.1 Kind.Entry与Kind.Return
@OnMethod(clazz="java.net.ServerSocket", method="bind" )
不写Location,默认就是刚进入函数的时候(Kind.ENTRY)。
但如果你想获得函数的返回结果或执行时间,则必须把拦截点定在返回时。

OnMethod(clazz = "java.net.ServerSocket", method = "getLocalPort", location = @Location(Kind.RETURN))
public static void onGetPort(@Return int port, @Duration long duration)
duration的单位是纳秒,要除以 1,000,000 才是毫秒。

3.2 Kind.Error, Kind.Throw和 Kind.Catch
Throw:异常抛出,Catch:异常被捕获,Error:异常没被捕获而被抛出函数之外,主要用于对某些异常情况的跟踪。
在参数定义里注入一个Throwable的参数,代表异常。
@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(Kind.ERROR))
public static void onBind(Throwable exception, @Duration long duration)

3.3 Kind.Call与Kind.Line
下例定义监控bind()函数里调用的所有其他函数:
@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER))
public static void onBind(@Self Object self, @TargetInstance Object instance, @TargetMethodOrField String method, @Duration long duration)

所调用的类及方法名所注入到@TargetInstance与 @TargetMethodOrField中。
静态函数中,instance的值为空。如果想获得执行时间,必须把Where定义成AFTER
注意这里,一定不要像下面这样大范围的匹配,否则这性能是神仙也没法救了
@OnMethod(clazz = "/javax\\.swing\\..*/", method = "/.*/", location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/"))

下例代码监控是否到达了Socket类的第363行。@OnMethod(clazz = "java.net.ServerSocket", location = @Location(value = Kind.LINE, line = 363))
public static void onBind4(int line) {

println("socket bind reach line:363");

}

line还可以为-1,然后每行都会打印出来,加参数int line 获得的当前行数。此时会显示函数里完整的执行路径,但肯定又非常慢。

4. 打印this,参数 与 返回值
4.1 定义参数注入
import com.sun.btrace.AnyType;
@OnMethod(clazz = "java.io.File", method = "createTempFile", location = @Location(value = Kind.RETURN))
public static void o(@Self Object self, String prefix, String suffix, @Return AnyType result)
如果想打印它们,首先按顺序定义用@Self 注释的this, 完整的参数列表,以及用@Return 注释的返回值。
需要打印哪个就定义哪个,不需要的就不要定义。但定义一定要按顺序,比如参数列表不能跑到返回值的后面。
1. Self:
如果是静态函数, self为空。
前面提到,如果上述使用了非JDK的类,命令行里要指定classpath。不过,如前所述,因为BTrace里不允许调用类的方法,所以定义具体类很多时候也没意思,所以self定义为Object就够了。

2. 参数:
参数数列表要么不要定义,要定义就要定义完整,否则BTrace无法处理不同参数的同名函数。
如果有些参数你实在不想引入非JDK类,又不会造成同名函数不可区分,可以用AnyType来定义(不能用Object)。
如果拦截点用正则表达式中匹配了多个函数,函数之间的参数个数不一样,你又还是想把参数打印出来时,可以用AnyType[] args来定义。
但不知道是不是当前版本的bug,AnyType[] args 不能和 location=Kind.RETURN 同用,否则会进入一种奇怪的静默状态,只要有一个函数定义错了,整个BTrace就什么都打印不出来。

3.返回值:
同理,结果也可以用AnyType来定义,特别是用正则表达式匹配多个函数的时候,连void都可以表示。
4.2 打印
再次强调,为了保证性能不受影响,BTrace不允许调用任何实例方法。
比如不能调用getter方法(怕在getter里有复杂的计算),只能通过直接反射来读取属性名。
又比如,除了JDK类,其他类toString时只会打印其类名+System.IdentityHashCode。
println, printArray,都按上面的规律进行,所以只能打打基本类型。
如果想打印一个Object的所有属性,用printFields()来反射。
如果只想反射某个属性,参照下面打印Port属性的写法。从性能考虑,应把field用静态变量缓存起来。
注意JDK类与非JDK类的区别:

import java.lang.reflect.Field;
//JDK的类这样写就行
private static Field fdFiled = field("java.io,FileInputStream", "fd");
//非JDK的类,要给出ClassLoader,否则ClassNotFound
private static Field portField = field(classForName("com.vip.demo.MyObject", contextClassLoader()), "port");
public static void onChannelRead(@Self Object self) {
println("port:" + getInt(portField, self));
}
4.3.TLS,拦截函数们之间间的通信机制
如果要多个拦截函数之间要通信,可以使用@TLS定义 ThreadLocal的变量来共享@TLS
private static int port = -1;
@OnMethod(clazz = "java.net.ServerSocket", method = "<init>")
public static void onServerSocket(int p){
port = p;
}
@OnMethod(clazz = "java.net.ServerSocket", method = "bind")
public static void onBind(){
println("server socket at " + port);
}
5. 典型场景
5.1 打印慢调用
下例打印所有用时超过1毫秒的Filter@OnMethod(clazz = "+com.vip.demo.Filter", method = "doFilter", location = @Location(Kind.RETURN))
public static void onDoFilter2(@ProbeClassName String pcn, @Duration long duration) {
if (duration > 1000000) {
println(pcn + ",duration:" + (duration / 100000));
}
}

最好能抽取打印耗时的函数,减少代码重复度。
定位到某一个Filter慢了之后,可以直接用Location(Kind.CALL),进一步找出它里面的哪一步慢了。

5.2 谁调用了这个函数
比如,谁调用了System.gc() ?@OnMethod(clazz = "java.lang.System", method = "gc")
public static void onSystemGC() {
println("entered System.gc()");
jstack();
}
 5.3 捕捉异常,或进入了某个特定代码行时,this对象及参数的值
按之前的提示,自己组合一下即可。

5.4 打印函数的调用的统计信息
如果你已经看到了这里,那基本也不用我再啰嗦了,自己看Samples里的Histogram.java 和 HistoOnEvent.java
可以用AtomicInteger构造计数器,然后定时(@OnTimer),或根据事件(@OnEvent)输出结果,ctrl+c后选择发送事件。

6. 小结
恭喜你最后看到这里,成为了一个及格的BTrace小工,从此线上Java地界里的事情,没有瞒得过你的了。 查看全部
BTrace是神器,每一个需要每天解决线上问题,但完全不用BTrace的Java工程师,都是可疑的 -- 凯尔文. 萧
BTrace的最大好处,是可以通过自己编写的脚本,获取应用的一切调用信息。而不需要不断地修改代码,加入System.out.println(), 然后重启,然后重启,然后重启应用!!!
同时,特别严格的约束,保证自己的消耗特别小,只要定义脚本时不作大死,直接在生产环境打开也没影响。
在网上搜索BTrace出来的文章都有点旧了,而且不够详细,于是决定,重新写一份,这个版本顺便把代码改成可C&P的。

1. 概述
1.1 快速开始
BTrace搬家了!! 已经从 Sun 搬到了http://github.com/btraceio/btrace,当前版本已是1.38。
在Release页面里下载最新Zip版,解压就能用,UserGuide和Samples也在里面。
先抄一个UserGuide里的例子:
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class HelloWorld {
@OnMethod(clazz="java.lang.Thread", method="start")
public static void onThreadStart() {
println("thread start!");
}
}
然后ps找出要监控的java应用的pid, ./btrace $pid HelloWorld.java 就跑起来了。
是不是很简单??基本上不用任何BTrace的知识,都能猜出HelloWorld会干啥。通过JVM Attach API,BTrace把自己绑进了被监控的进程,按HelloWorld.java里的定义,进行AOP式的代码植入。
最开心就是这里,如果还想监控其他内容,直接修改HelloWorld.java,再执行一次btrace就可以了,不需要重启应用!! 重启应用!!
1.2 典型的场景
1. 服务慢,能找出慢在哪一步,哪个函数里么?
2. 谁调用了System.gc(),调用栈为何?
3. 谁构造了一个超大的ArrayList?
4. 什么样的入参或对象属性,导致抛出了这个异常?或进入了这个处理分支?
1.3 一些重要事情
为了避免BTrace脚本的消耗过大影响真正业务,所以定义了一系列不允许的事情:比如只能调用BTraceUtils 里的一系列方法和脚本里定义的static方法,不允许其他调用任何类的任何方法。 比如不允许创建对象,比如不允许For 循环等等,更多规定看User Guide。 
当然,可以用-u 运行在unsafe mode来规避限制,但不推荐。
在以前的例子里,甚至还不能字符串相加,必须用strcat:
println(strcat(strcat(probeClass, "."), probeMethod));
好在新版里已经可以写回:
println(probeClass + '.' + probeMethod);
另外,BTrace植入过的代码,会一直在,直到应用重启为止。所以即使BTrace退出了,业务函数每次执行时都会多出一次BTrace是否Attach状态的判断。
最后,记得用Eclipse,而不是写字板来写脚本。

1.4 其他命令行选项
1.4.1 定义classpath
如果在HelloWorld.java里使用了JDK外的其他类,比如Netty的:
./btrace -cp .:netty-all-4.0.41.Final.jar $pid HelloWorld.java
但上面定义的classpath只在编译脚本时使用,而脚本里需要显式使用非JDK类的机会其实很少(后面真正用到的时候会提起)。
而在运行时,因为已经绑到目标应用的JVM里,用的是目标JVM的classpath。 
1.4.2 结果输出到文件
./btrace -o mylog $pid HelloWorld.java
很坑新人的参数,首先,这个mylog会生成在应用的启动目录,而不是btrace的启动目录。其次,执行过一次-o之后,再执行btrace不加-o 也不会再输出回console,直到应用重启为止。
所以最好直接用转向了事:
./btrace $pid HelloWorld.java > mylog
1.4.3.预编译脚本
虽然btrace可以实时编译Java源文件,但如果你的脚本是要给运维同学执行的,线上运行时才发现写错了就尴尬了。此时可以用btracec预编译一下:
./btracec HelloWorld.java
接下来,开始一步步讲解脚本的编写。 

2. 拦截方法定义

2.1 精准定位
就是HelloWorld的例子,精确定义要监控的类名与方法名。
 
2.2 正则表达式定位
批量定义需要监控的类与方法。正则表达式需要写在两个 "/" 中间。
下例监控javax.swing下的所有类的所有方法....会非常慢,实际使用时范围还是要窄些。
@OnMethod(clazz="/javax\\.swing\\..*/", method="/.*/")
public static void swingMethods( @ProbeClassName String probeClass, @ProbeMethodName String probeMethod) {
print("entered " + probeClass + "." + probeMethod);
}
通过在拦截函数的参数定义里注入@ProbeClassName和 @ProbeMethodName参数,告诉脚本实际匹配到的类和方法名。
另一个例子,监控Statement的executeUpdate(), executeQuery() 和 executeBatch() 三个方法:method = "/execute($|Update|Query|Batch)/"

2.3 按接口,父类,Annotation定位
要匹配所有Filter类,在接口或父类的名称前面,加个 "+" 就行
@OnMethod(clazz="+com.vip.demo.Filter", method="doFilter")
也可以按类或方法上的annotaiton匹配,前面加个@就行
@OnMethod(clazz="@javax.jws.WebService", method="@javax.jws.WebMethod")

2.4 其他
1. 构造函数的名字是 "<init>"
@OnMethod(clazz="java.net.ServerSocket", method="<init>")

2. 静态内部类的写法,是在类与内部类之间加上"$"
@OnMethod(clazz="co m.vip.MyServer$MyInnerClass", method="run")
3.如果有多个同名的函数,想区分开来,可以在拦截函数上定义不同的参数列表(见4.1)。
3. 拦截时机
可以为同一个函数的不同Location,分别定义多个拦截函数。
3.1 Kind.Entry与Kind.Return
@OnMethod(clazz="java.net.ServerSocket", method="bind" )
不写Location,默认就是刚进入函数的时候(Kind.ENTRY)。
但如果你想获得函数的返回结果或执行时间,则必须把拦截点定在返回时。

OnMethod(clazz = "java.net.ServerSocket", method = "getLocalPort", location = @Location(Kind.RETURN))
public static void onGetPort(@Return int port, @Duration long duration)
duration的单位是纳秒,要除以 1,000,000 才是毫秒。

3.2 Kind.Error, Kind.Throw和 Kind.Catch
Throw:异常抛出,Catch:异常被捕获,Error:异常没被捕获而被抛出函数之外,主要用于对某些异常情况的跟踪。
在参数定义里注入一个Throwable的参数,代表异常。
@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(Kind.ERROR))
public static void onBind(Throwable exception, @Duration long duration)

3.3 Kind.Call与Kind.Line
下例定义监控bind()函数里调用的所有其他函数:
@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER))
public static void onBind(@Self Object self, @TargetInstance Object instance, @TargetMethodOrField String method, @Duration long duration)

所调用的类及方法名所注入到@TargetInstance与 @TargetMethodOrField中。
静态函数中,instance的值为空。如果想获得执行时间,必须把Where定义成AFTER
注意这里,一定不要像下面这样大范围的匹配,否则这性能是神仙也没法救了
@OnMethod(clazz = "/javax\\.swing\\..*/", method = "/.*/", location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/"))

下例代码监控是否到达了Socket类的第363行。
@OnMethod(clazz = "java.net.ServerSocket", location = @Location(value = Kind.LINE, line = 363))
public static void onBind4(int line) {

println("socket bind reach line:363");

}

line还可以为-1,然后每行都会打印出来,加参数int line 获得的当前行数。此时会显示函数里完整的执行路径,但肯定又非常慢。

4. 打印this,参数 与 返回值
4.1 定义参数注入
import com.sun.btrace.AnyType;
@OnMethod(clazz = "java.io.File", method = "createTempFile", location = @Location(value = Kind.RETURN))
public static void o(@Self Object self, String prefix, String suffix, @Return AnyType result)
如果想打印它们,首先按顺序定义用@Self 注释的this, 完整的参数列表,以及用@Return 注释的返回值。
需要打印哪个就定义哪个,不需要的就不要定义。但定义一定要按顺序,比如参数列表不能跑到返回值的后面。
1. Self:
如果是静态函数, self为空。
前面提到,如果上述使用了非JDK的类,命令行里要指定classpath。不过,如前所述,因为BTrace里不允许调用类的方法,所以定义具体类很多时候也没意思,所以self定义为Object就够了。

2. 参数:
参数数列表要么不要定义,要定义就要定义完整,否则BTrace无法处理不同参数的同名函数。
如果有些参数你实在不想引入非JDK类,又不会造成同名函数不可区分,可以用AnyType来定义(不能用Object)。
如果拦截点用正则表达式中匹配了多个函数,函数之间的参数个数不一样,你又还是想把参数打印出来时,可以用AnyType[] args来定义。
但不知道是不是当前版本的bug,AnyType[] args 不能和 location=Kind.RETURN 同用,否则会进入一种奇怪的静默状态,只要有一个函数定义错了,整个BTrace就什么都打印不出来。

3.返回值:
同理,结果也可以用AnyType来定义,特别是用正则表达式匹配多个函数的时候,连void都可以表示。
4.2 打印
再次强调,为了保证性能不受影响,BTrace不允许调用任何实例方法。
比如不能调用getter方法(怕在getter里有复杂的计算),只能通过直接反射来读取属性名。
又比如,除了JDK类,其他类toString时只会打印其类名+System.IdentityHashCode。
println, printArray,都按上面的规律进行,所以只能打打基本类型。
如果想打印一个Object的所有属性,用printFields()来反射。
如果只想反射某个属性,参照下面打印Port属性的写法。从性能考虑,应把field用静态变量缓存起来。
注意JDK类与非JDK类的区别:

import java.lang.reflect.Field;
//JDK的类这样写就行
private static Field fdFiled = field("java.io,FileInputStream", "fd");
//非JDK的类,要给出ClassLoader,否则ClassNotFound
private static Field portField = field(classForName("com.vip.demo.MyObject", contextClassLoader()), "port");
public static void onChannelRead(@Self Object self) {
println("port:" + getInt(portField, self));
}

4.3.TLS,拦截函数们之间间的通信机制
如果要多个拦截函数之间要通信,可以使用@TLS定义 ThreadLocal的变量来共享
@TLS
private static int port = -1;
@OnMethod(clazz = "java.net.ServerSocket", method = "<init>")
public static void onServerSocket(int p){
port = p;
}
@OnMethod(clazz = "java.net.ServerSocket", method = "bind")
public static void onBind(){
println("server socket at " + port);
}

5. 典型场景
5.1 打印慢调用
下例打印所有用时超过1毫秒的Filter
@OnMethod(clazz = "+com.vip.demo.Filter", method = "doFilter", location = @Location(Kind.RETURN))
public static void onDoFilter2(@ProbeClassName String pcn, @Duration long duration) {
if (duration > 1000000) {
println(pcn + ",duration:" + (duration / 100000));
}
}

最好能抽取打印耗时的函数,减少代码重复度。
定位到某一个Filter慢了之后,可以直接用Location(Kind.CALL),进一步找出它里面的哪一步慢了。

5.2 谁调用了这个函数
比如,谁调用了System.gc() ?
@OnMethod(clazz = "java.lang.System", method = "gc")
public static void onSystemGC() {
println("entered System.gc()");
jstack();
}

 5.3 捕捉异常,或进入了某个特定代码行时,this对象及参数的值
按之前的提示,自己组合一下即可。

5.4 打印函数的调用的统计信息
如果你已经看到了这里,那基本也不用我再啰嗦了,自己看Samples里的Histogram.java 和 HistoOnEvent.java
可以用AtomicInteger构造计数器,然后定时(@OnTimer),或根据事件(@OnEvent)输出结果,ctrl+c后选择发送事件。

6. 小结
恭喜你最后看到这里,成为了一个及格的BTrace小工,从此线上Java地界里的事情,没有瞒得过你的了。

关键业务系统的JVM参数推荐(2016热冬版)

admin 发表了文章 • 0 个评论 • 155 次浏览 • 2017-01-17 10:27 • 来自相关话题

在关键的业务系统的JVM参数调优,除了继续追求技术人员最爱的高吞吐与低延时之外,系统的稳定性与出现问题时排查的便捷性也很重要。

这是本文的一个原则,后面也会一次又一次的强调,所以与网上其他的文章略有不同,请调优高手和运维老大们多指引。
前言1,资料
学习开源项目的启动脚本是个不错的主意,比如Cassandra家的, 附送一篇解释它的文章。
JVM调优的"标准参数"的各种陷阱 R大的文章,在JDK6时写的,期待更新。
偶然翻到Linkedin工程师的一篇文章。
更偶然翻到的一份不错的参数列表。

前言2, -XX:+PrintFlagsFinal打印参数值
当你在网上兴冲冲找到一个可优化的参数时,先用-XX: +PrintFlagsFinal看看,它可能已经默认打开了,再找到一个,还是默认打开了...

JDK7与JDK8,甚至JDK7中的不同版本,有些参数值都不一样,所以不要轻信网上任何文章,一切以生产环境同版本的JDK打出来的为准。

经常以类似下面的语句去查看参数,偷懒不起应用,用-version代替。有些参数设置后会影响其他参数,所以查看时也把它带上。

java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads 

前言3,关于默认值
JDK8会默认打开-XX:+TieredCompilation多层编译,而JDK7则不会。JDK7u40以后的版本会默认打开-XX:+OptimizeStringConcat优化字符串拼接,而之前的则不打开。

对于这些参数,我的建议是顺势而为,JDK在那个版本默认打开不打开总有它的理由。安全第一,没有很好的因由,不要随便因为网上某篇文章的推荐(包括你现在在读的这篇)就去设置。

1. 性能篇
先写一些不那么常见的,后面再来老生常谈。
1.1 取消偏向锁 -XX:-UseBiasedLocking
JDK1.6开始默认打开的偏向锁,会尝试把锁赋给第一个访问它的线程,取消同步块上的synchronized原语。如果始终只有一条线程在访问它,就成功略过同步操作以获得性能提升。
但一旦有第二条线程访问这把锁,JVM就要撤销偏向锁恢复到未锁定线程的状态,详见 JVM的Stop The World,安全点,黑暗的地底世界, 可以看到不少RevokeBiasd的纪录,像GC一样,会Stop The World的干活,虽然只是很短很短的停顿,但对于多线程并发的应用,取消掉它反而有性能的提升和延时的极微的缩短,所以Cassandra就取消了它。
1.2 -XX:AutoBoxCacheMax=20000
Integer i = 3;这语句有着 int自动装箱成Integer的过程,JDK默认只缓存 -128 ~ +127的int 和 long,超出范围的数字就要即时构建新的Integer对象。设为20000后,我们应用的QPS从48,000提升到50,000,足足4%的影响。详见Java Integer(-128~127)值的==和equals比较产生的思考
1.3 启动时访问并置零内存页面-XX:+AlwaysPreTouch
启动时就把参数里说好了的内存全部舔一遍,可能令得启动时慢上一点,但后面访问时会更流畅,比如页面会连续分配,比如不会在晋升新生代到老生代时才去访问页面使得GC停顿时间加长。不过这选项对大堆才会更有感觉一点。
 
1.4 -XX:+PerfDisableSharedMem
Cassandra家的一个参数,一直没留意,直到发生高IO时的JVM停顿。原来JVM经常会默默的在/tmp/hperf 目录写上一点statistics数据,如果刚好遇到PageCache刷盘,把文件阻塞了,就不能结束这个Stop the World的安全点了。用此参数可以禁止JVM写statistics数据,代价是jps, jstat 用不了,只能用JMX取数据。有时用JMX取新生代老生代使用百分比还真没jstat方便。详见The Four Month Bug: JVM statistics cause garbage collection pauses
 
1.5 -Djava.security.egd=file:/dev/./urandom
此江湖偏方原用于Tomcat显式使用SHA1PRNG算法时,初始因子从/dev/random读取导致堵塞。而使用此设置后,额外效果是默认的SecureRandom算法也变成SHA1了。 SHA1PRNG 比 NativePRNG消耗小一半,synchronized的代码少一半,所以没特殊安全要求的话建议用SHA1。详见 SecureRandom的江湖偏方与真实效果
 
1.6 不建议的参数
1.-XX:+AggressiveOpts是一些还没默认打开的优化参数集合, -XX:AutoBoxCacheMax是其中的一项。但如前所述,关键系统里不建议打开。虽然通过-XX:+AggressiveOpts 与 -XX:-AggressiveOpts 的对比,目前才改变了三个参数,但为免以后某个版本的JDK里默默改变更多激进的配置,还是不要了。

2.Linkined那种黑科技,先要解锁VMOptions才能配置的就更不用说了,比如
-XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=32768

3. JIT Compile相关的参数,函数调用多少次之后开始编译的阀值,内联函数大小的阀值等等,不要乱改了。

4. -XX:+UseFastAccessorMethods,JDK6的优化,据说在多层编译下还慢了,所以是默认关闭的。

5. -server,在64位linux中,你想设成-client都不行的,所以写了也是白写。

1.7 可选参数
1. -Djava.awt.headless=true,如果服务器上没有屏幕,键盘,鼠标,又需要用到它们的时候,详见在 Java SE 平台上使用 Headless 模式
2. -XX:-UseCounterDecay,禁止JIT调用计数器衰减。默认情况下,每次GC时会对调用计数器进行砍半的操作,导致有些方法一直是个温热,可能永远都达不到C2编译的1万次的阀值。
3. -XX:-TieredCompilation,禁止JDK8默认的多层编译,在某些情况下因为有些方法C1编译后C2不再编译,多层编译反而比C2编译慢,如果发现此情况可进行禁止。

2. GC篇
2.1 GC策略
为了稳健,还是8G以下的堆还是CMS好了,G1的细节实现起来难度太大,从理论提出到现在都做了六七年了。
CMS真正可设的东西也不多,详见JVM实用参数(七)CMS收集器

1.基本配置
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
因为我们的监控系统会通过JMX监控内存达到90%的状况(留点处理的时间),所以设置让它75%就开始跑了,早点开始也能避免Full GC等意外情况(概念重申,这种主动的CMS GC,和JVM的老生代、永久代、堆外内存完全不能分配内存了而强制Full GC是不同的概念)。为了让这个设置生效,还要设置-XX:+UseCMSInitiatingOccupancyOnly,否则75只被用来做开始的参考值,后面还是JVM自己算。

2. -XX:MaxTenuringThreshold=2,这是GC里改动效果最明显的一个参数了。对象在Survivor区熬过多少次Young GC后晋升到年老代,JDK7里看起来默认是6,跑起来好像变成了15。
Young GC是最大的应用停顿来源,而新生代里GC后存活对象的多少又直接影响停顿的时间,所以如果清楚Young GC的执行频率和应用里大部分临时对象的最长生命周期,可以把它设的更短一点,让其实不是临时对象的新生代长期对象赶紧晋升到年老代,别呆着。
用-XX:+PrintTenuringDistribution观察下,如果后面几代都差不多,就可以设小,比如JMeter里是2。而我们的两个系统里一个设了2,一个设了6。

3. -XX:+ExplicitGCInvokesConcurrent, 但不要-XX:+DisableExplicitGC, 比如Netty之堆外内存扫盲篇,可见禁了system.gc() 未必是好事,只要自己的代码里没有调它,也没用什么特别烂的类库,真有人调了总有调的原因。-XX+ExplicitGCInvokesConcurrent 则在full gc时,并不全程停顿,依然只在ygc和两个remark阶段停顿,详见JVM源码分析之SystemGC完全解读

4. -XX:ParallelRefProcEnabled , 默认为false,并行的处理Reference对象,如WeakReference,除非在GC log里出现Reference处理时间较长的日志,否则效果不会很明显,但我们总是要JVM尽量的并行,所以设了也就设了。
2.2 GC里不建议设的参数
1. -XX:+CMSClassUnloadingEnabled,在CMS中清理永久代中的过期的Class而不等到Full GC,JDK7默认关闭而JDK8打开。看自己情况,比如有没有运行动态语言脚本如Groovy产生大量的临时类。它会增加CMS remark的暂停时间,所以如果新类加载并不频繁,这个参数还是不开的好。

2. 用了CMS,新生代收集默认就是-XX:+UseParNewGC,不用自己设。

3. 并发收集线程数
ParallelGCThreads=8+( Processor - 8 ) ( 5/8 ),
ConcGCThreads = (ParallelGCThreads + 3)/4
比如双CPU,六核,超线程就是24个处理器,小于8个处理器时ParallelGCThreads按处理器数量,大于时按上述公式ParallelGCThreads=18, ConcGCThreads=5。除了一些不在乎停顿时间的后台辅助程序会特意把它减少,平时不建议动。

4. -XX:+CMSScavengeBeforeRemark,默认为关闭,在CMS remark前,先执行一次minor GC将新生代清掉,这样从老生代的对象引用到的新生代对象的个数就少了,停止全世界的CMS remark阶段就短一些。如果看到GC日志里remark阶段的时间超长,可以打开此项看看有没有效果,否则还是不要打开了,白白多了次YGC。

5. -XX:CMSFullGCsBeforeCompaction,默认为0,即每次full gc都对老生代进行碎片整理压缩。Full GC 不同于 前面设置的75%老生代时触发的CMS GC,只在System.gc(),老生代达到100%,老生代碎片过大无法分配空间给新晋升的大对象这些特殊情况里发生,所以设为每次都进行碎片整理是合适的,详见此贴里R大的解释。

2.3 内存大小的设置

这些关于大小的参数,给人感觉是最踏实可控的。
其实JVM除了显式设置的-Xmx堆内存,还有一堆其他占内存的地方(堆外内存,线程栈,永久代,二进制代码cache),在容量规划的时候要留意。
关键业务系统的服务器上内存一般都是够的,所以尽管设得宽松点。
1. -Xmx, -Xms, 堆内存大小,2~4G均可,再大了注意GC时间。
2. -Xmn or -XX:NewSize and -XX:MaxNewSize or -XX:NewRatio, JDK默认新生代占堆大小的1/3, 个人喜欢把对半分, 增大新生代的大小,能减少GC的频率(但也会加大每次GC的停顿时间),主要是看老生代里没多少长期对象的话,占2/3太多了。可以用-Xmn 直接赋值(等于-XX:NewSize and -XX:MaxNewSize同值的缩写),或把NewRatio设为1来对半分(但如果想设置新生代比老生代大就只能用-Xmn)。

3. -XX:PermSize=128m -XX:MaxPermSize=512m (JDK7)现在的应用有Hibernate/Spring这些闹腾的家伙AOP之后类都比较多,可以一开始就把初始值从64M设到128M,并设一个更大的Max值以求保险。

4. -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m(JDK8),JDK8的永生代几乎可用完机器的所有内存,同样设一个128M的初始值,512M的最大值保护一下。

2.4 其他内存大小等可选设置
1. -XX:SurvivorRatio 新生代中每个存活区的大小,默认为8,即1/10的新生代 1/(SurvivorRatio+2),有人喜欢设小点省点给新生代,但要避免太小使得存活区放不下临时对象而要晋升到老生代,还是从GC Log里看实际情况了。

2. -Xss 在堆之外,线程占用栈内存,默认每条线程为1M(以前是256K)。存放方法调用出参入参的栈,局部变量,标量替换后掉局部变量等,有人喜欢设小点节约内存开更多线程。但反正内存够也就不必要设小,有人喜欢再设大点,特别是有JSON解析之类的递归调用时不能设太小。

3. -XX:MaxDirectMemorySize,堆外内存/直接内存的大小,默认为Heap区总内存减去一个Survivor区的大小,详见Netty之堆外内存扫盲篇。

4. -XX:ReservedCodeCacheSize, JIT编译后二进制代码的存放区,满了之后就不再编译。JDK7默认不开多层编译48M,开了96M,而JDK8默认开多层编译240M。可以在JMX里看看CodeCache的大小,JDK7下的48M一般够了,也可以把它设大点,反正内存多。
 
2.5 GC日志
1.基本配置
-Xloggc:/dev/shm/gc-myapplication.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails
详见JVM实用参数(八)GC日志,有人担心写GC日志会影响性能,但测试下来实在没什么影响,还是留一份用来排查好。
到后来,又发现如果遇上高IO的情况,如果GC的时候,操作系统正在flush pageCache 到磁盘,也可能导致GC log文件被锁住,从而让GC结束不了。所以把它指向了/dev/shm 这种内存中文件系统,避免这种停顿,详见Eliminating Large JVM GC Pauses Caused by Background IO Traffic
用+PrintGCDateStamps而不是PrintGCTimeStamps,打印可读的日期而不是时间戳。

2. -XX:+PrintGCApplicationStoppedTime,它的名字没起好,它除了打印清晰的GC停顿时间外,还可以打印其他的停顿时间,比如取消偏向锁,class 被agent redefine,code deoptimization等等,有助于发现一些原来没想到的问题,建议也加上。如果真的发现了一些不知什么的停顿,再临时加上"-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1" 找原因。

3. GC日志默认会在重启后清空,但有人担心长期运行不重启的应用会把文件弄得很大,有"-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1M"的参数可以让日志滚动起来。但重启后的文件名太混乱太让人头痛,所以还是不加。
 
3. 监控篇
JVM输出的各种日志,如果未指定路径,通常会生成到运行应用的相同目录,为了避免有时候在不同的地方执行启动脚本,一般将日志路径集中设到一个固定的地方。
3.1 -XX:+PrintCommandLineFlags
运维有时会对启动参数做一些临时的更改,将每次启动的参数输出到stdout,将来有据可查。
打印出来的是命令行里设置了的参数以及因为这些参数隐式影响的参数,比如开了CMS后,-XX:+UseParNewGC也被自动打开。
3.2 -XX:-OmitStackTraceInFastThrow
为异常设置StackTrace是个昂贵的操作,所以当应用在相同地方抛出相同的异常N次(两万?)之后,JVM会对某些特定异常如NPE,数组越界等进行优化,不再带上异常栈。此时,你可能会看到日志里一条条Nul Point Exception,而真正输出完整栈的日志早被滚动到不知哪里去了,也就完全不知道这NPE发生在什么地方,欲哭无泪。 所以,将它禁止吧。
3.3 coredump与 -XX:ErrorFile
JVM crash时,hotspot 会生成一个error文件,提供JVM状态信息的细节。如前所述,将其输出到固定目录,避免到时会到处找这文件。文件名中的%p会被自动替换为应用的PID
-XX:ErrorFile=${MYLOGDIR}/hs_err_%p.log
当然,更好的做法是生成coredump,从CoreDump能够转出Heap Dump 和 Thread Dump 还有crash的地方,非常实用。
在启动脚本里加上 ulimit -c unlimited或其他的设置方式,如果有root权限,设一下输出目录更好
echo "/{MYLOGDIR}/coredump.%p" > /proc/sys/kernel/core_pattern
什么?你不知道这coredump有什么用?看来你是没遇过JVM Segment Fault的幸福人。
 
3.4 -XX:+HeapDumpOnOutOfMemoryError
在Out Of Memory,JVM快死快死掉的时候,输出Heap Dump到指定文件。不然开发很多时候还真不知道怎么重现错误。
路径只指向目录,JVM会保持文件名的唯一性,叫java_pid${pid}.hprof。如果指向文件,而文件已存在,反而不能写入。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOGDIR}/

3.5 JMX
-Dcom.sun.management.jmxremote.port=${MY_JMX_PORT} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1
以上设置,只让本地的Zabbix之类监控软件通过JMX监控JVM,不允许远程访问。
老规矩,文章里的链接,全部点“阅读原文”再看。
祝大家生产环境里的JVM都稳健无比,永远没bug。 查看全部
在关键的业务系统的JVM参数调优,除了继续追求技术人员最爱的高吞吐与低延时之外,系统的稳定性与出现问题时排查的便捷性也很重要。

这是本文的一个原则,后面也会一次又一次的强调,所以与网上其他的文章略有不同,请调优高手和运维老大们多指引。
前言1,资料
学习开源项目的启动脚本是个不错的主意,比如Cassandra家的, 附送一篇解释它的文章。
JVM调优的"标准参数"的各种陷阱 R大的文章,在JDK6时写的,期待更新。
偶然翻到Linkedin工程师的一篇文章。
更偶然翻到的一份不错的参数列表。

前言2, -XX:+PrintFlagsFinal打印参数值
当你在网上兴冲冲找到一个可优化的参数时,先用-XX: +PrintFlagsFinal看看,它可能已经默认打开了,再找到一个,还是默认打开了...

JDK7与JDK8,甚至JDK7中的不同版本,有些参数值都不一样,所以不要轻信网上任何文章,一切以生产环境同版本的JDK打出来的为准。

经常以类似下面的语句去查看参数,偷懒不起应用,用-version代替。有些参数设置后会影响其他参数,所以查看时也把它带上。

java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads 

前言3,关于默认值
JDK8会默认打开-XX:+TieredCompilation多层编译,而JDK7则不会。JDK7u40以后的版本会默认打开-XX:+OptimizeStringConcat优化字符串拼接,而之前的则不打开。

对于这些参数,我的建议是顺势而为,JDK在那个版本默认打开不打开总有它的理由。安全第一,没有很好的因由,不要随便因为网上某篇文章的推荐(包括你现在在读的这篇)就去设置。

1. 性能篇
先写一些不那么常见的,后面再来老生常谈。
1.1 取消偏向锁 -XX:-UseBiasedLocking
JDK1.6开始默认打开的偏向锁,会尝试把锁赋给第一个访问它的线程,取消同步块上的synchronized原语。如果始终只有一条线程在访问它,就成功略过同步操作以获得性能提升。
但一旦有第二条线程访问这把锁,JVM就要撤销偏向锁恢复到未锁定线程的状态,详见 JVM的Stop The World,安全点,黑暗的地底世界, 可以看到不少RevokeBiasd的纪录,像GC一样,会Stop The World的干活,虽然只是很短很短的停顿,但对于多线程并发的应用,取消掉它反而有性能的提升和延时的极微的缩短,所以Cassandra就取消了它。
1.2 -XX:AutoBoxCacheMax=20000
Integer i = 3;这语句有着 int自动装箱成Integer的过程,JDK默认只缓存 -128 ~ +127的int 和 long,超出范围的数字就要即时构建新的Integer对象。设为20000后,我们应用的QPS从48,000提升到50,000,足足4%的影响。详见Java Integer(-128~127)值的==和equals比较产生的思考
1.3 启动时访问并置零内存页面-XX:+AlwaysPreTouch
启动时就把参数里说好了的内存全部舔一遍,可能令得启动时慢上一点,但后面访问时会更流畅,比如页面会连续分配,比如不会在晋升新生代到老生代时才去访问页面使得GC停顿时间加长。不过这选项对大堆才会更有感觉一点。
 
1.4 -XX:+PerfDisableSharedMem
Cassandra家的一个参数,一直没留意,直到发生高IO时的JVM停顿。原来JVM经常会默默的在/tmp/hperf 目录写上一点statistics数据,如果刚好遇到PageCache刷盘,把文件阻塞了,就不能结束这个Stop the World的安全点了。用此参数可以禁止JVM写statistics数据,代价是jps, jstat 用不了,只能用JMX取数据。有时用JMX取新生代老生代使用百分比还真没jstat方便。详见The Four Month Bug: JVM statistics cause garbage collection pauses
 
1.5 -Djava.security.egd=file:/dev/./urandom
此江湖偏方原用于Tomcat显式使用SHA1PRNG算法时,初始因子从/dev/random读取导致堵塞。而使用此设置后,额外效果是默认的SecureRandom算法也变成SHA1了。 SHA1PRNG 比 NativePRNG消耗小一半,synchronized的代码少一半,所以没特殊安全要求的话建议用SHA1。详见 SecureRandom的江湖偏方与真实效果
 
1.6 不建议的参数
1.-XX:+AggressiveOpts是一些还没默认打开的优化参数集合, -XX:AutoBoxCacheMax是其中的一项。但如前所述,关键系统里不建议打开。虽然通过-XX:+AggressiveOpts 与 -XX:-AggressiveOpts 的对比,目前才改变了三个参数,但为免以后某个版本的JDK里默默改变更多激进的配置,还是不要了。

2.Linkined那种黑科技,先要解锁VMOptions才能配置的就更不用说了,比如
-XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=32768

3. JIT Compile相关的参数,函数调用多少次之后开始编译的阀值,内联函数大小的阀值等等,不要乱改了。

4. -XX:+UseFastAccessorMethods,JDK6的优化,据说在多层编译下还慢了,所以是默认关闭的。

5. -server,在64位linux中,你想设成-client都不行的,所以写了也是白写。

1.7 可选参数
1. -Djava.awt.headless=true,如果服务器上没有屏幕,键盘,鼠标,又需要用到它们的时候,详见在 Java SE 平台上使用 Headless 模式
2. -XX:-UseCounterDecay,禁止JIT调用计数器衰减。默认情况下,每次GC时会对调用计数器进行砍半的操作,导致有些方法一直是个温热,可能永远都达不到C2编译的1万次的阀值。
3. -XX:-TieredCompilation,禁止JDK8默认的多层编译,在某些情况下因为有些方法C1编译后C2不再编译,多层编译反而比C2编译慢,如果发现此情况可进行禁止。

2. GC篇
2.1 GC策略
为了稳健,还是8G以下的堆还是CMS好了,G1的细节实现起来难度太大,从理论提出到现在都做了六七年了。
CMS真正可设的东西也不多,详见JVM实用参数(七)CMS收集器

1.基本配置
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
因为我们的监控系统会通过JMX监控内存达到90%的状况(留点处理的时间),所以设置让它75%就开始跑了,早点开始也能避免Full GC等意外情况(概念重申,这种主动的CMS GC,和JVM的老生代、永久代、堆外内存完全不能分配内存了而强制Full GC是不同的概念)。为了让这个设置生效,还要设置-XX:+UseCMSInitiatingOccupancyOnly,否则75只被用来做开始的参考值,后面还是JVM自己算。

2. -XX:MaxTenuringThreshold=2,这是GC里改动效果最明显的一个参数了。对象在Survivor区熬过多少次Young GC后晋升到年老代,JDK7里看起来默认是6,跑起来好像变成了15。
Young GC是最大的应用停顿来源,而新生代里GC后存活对象的多少又直接影响停顿的时间,所以如果清楚Young GC的执行频率和应用里大部分临时对象的最长生命周期,可以把它设的更短一点,让其实不是临时对象的新生代长期对象赶紧晋升到年老代,别呆着。
用-XX:+PrintTenuringDistribution观察下,如果后面几代都差不多,就可以设小,比如JMeter里是2。而我们的两个系统里一个设了2,一个设了6。

3. -XX:+ExplicitGCInvokesConcurrent, 但不要-XX:+DisableExplicitGC, 比如Netty之堆外内存扫盲篇,可见禁了system.gc() 未必是好事,只要自己的代码里没有调它,也没用什么特别烂的类库,真有人调了总有调的原因。-XX+ExplicitGCInvokesConcurrent 则在full gc时,并不全程停顿,依然只在ygc和两个remark阶段停顿,详见JVM源码分析之SystemGC完全解读

4. -XX:ParallelRefProcEnabled , 默认为false,并行的处理Reference对象,如WeakReference,除非在GC log里出现Reference处理时间较长的日志,否则效果不会很明显,但我们总是要JVM尽量的并行,所以设了也就设了。
2.2 GC里不建议设的参数
1. -XX:+CMSClassUnloadingEnabled,在CMS中清理永久代中的过期的Class而不等到Full GC,JDK7默认关闭而JDK8打开。看自己情况,比如有没有运行动态语言脚本如Groovy产生大量的临时类。它会增加CMS remark的暂停时间,所以如果新类加载并不频繁,这个参数还是不开的好。

2. 用了CMS,新生代收集默认就是-XX:+UseParNewGC,不用自己设。

3. 并发收集线程数
ParallelGCThreads=8+( Processor - 8 ) ( 5/8 ),
ConcGCThreads = (ParallelGCThreads + 3)/4
比如双CPU,六核,超线程就是24个处理器,小于8个处理器时ParallelGCThreads按处理器数量,大于时按上述公式ParallelGCThreads=18, ConcGCThreads=5。除了一些不在乎停顿时间的后台辅助程序会特意把它减少,平时不建议动。

4. -XX:+CMSScavengeBeforeRemark,默认为关闭,在CMS remark前,先执行一次minor GC将新生代清掉,这样从老生代的对象引用到的新生代对象的个数就少了,停止全世界的CMS remark阶段就短一些。如果看到GC日志里remark阶段的时间超长,可以打开此项看看有没有效果,否则还是不要打开了,白白多了次YGC。

5. -XX:CMSFullGCsBeforeCompaction,默认为0,即每次full gc都对老生代进行碎片整理压缩。Full GC 不同于 前面设置的75%老生代时触发的CMS GC,只在System.gc(),老生代达到100%,老生代碎片过大无法分配空间给新晋升的大对象这些特殊情况里发生,所以设为每次都进行碎片整理是合适的,详见此贴里R大的解释。

2.3 内存大小的设置

这些关于大小的参数,给人感觉是最踏实可控的。
其实JVM除了显式设置的-Xmx堆内存,还有一堆其他占内存的地方(堆外内存,线程栈,永久代,二进制代码cache),在容量规划的时候要留意。
关键业务系统的服务器上内存一般都是够的,所以尽管设得宽松点。
1. -Xmx, -Xms, 堆内存大小,2~4G均可,再大了注意GC时间。
2. -Xmn or -XX:NewSize and -XX:MaxNewSize or -XX:NewRatio, JDK默认新生代占堆大小的1/3, 个人喜欢把对半分, 增大新生代的大小,能减少GC的频率(但也会加大每次GC的停顿时间),主要是看老生代里没多少长期对象的话,占2/3太多了。可以用-Xmn 直接赋值(等于-XX:NewSize and -XX:MaxNewSize同值的缩写),或把NewRatio设为1来对半分(但如果想设置新生代比老生代大就只能用-Xmn)。

3. -XX:PermSize=128m -XX:MaxPermSize=512m (JDK7)现在的应用有Hibernate/Spring这些闹腾的家伙AOP之后类都比较多,可以一开始就把初始值从64M设到128M,并设一个更大的Max值以求保险。

4. -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m(JDK8),JDK8的永生代几乎可用完机器的所有内存,同样设一个128M的初始值,512M的最大值保护一下。

2.4 其他内存大小等可选设置
1. -XX:SurvivorRatio 新生代中每个存活区的大小,默认为8,即1/10的新生代 1/(SurvivorRatio+2),有人喜欢设小点省点给新生代,但要避免太小使得存活区放不下临时对象而要晋升到老生代,还是从GC Log里看实际情况了。

2. -Xss 在堆之外,线程占用栈内存,默认每条线程为1M(以前是256K)。存放方法调用出参入参的栈,局部变量,标量替换后掉局部变量等,有人喜欢设小点节约内存开更多线程。但反正内存够也就不必要设小,有人喜欢再设大点,特别是有JSON解析之类的递归调用时不能设太小。

3. -XX:MaxDirectMemorySize,堆外内存/直接内存的大小,默认为Heap区总内存减去一个Survivor区的大小,详见Netty之堆外内存扫盲篇。

4. -XX:ReservedCodeCacheSize, JIT编译后二进制代码的存放区,满了之后就不再编译。JDK7默认不开多层编译48M,开了96M,而JDK8默认开多层编译240M。可以在JMX里看看CodeCache的大小,JDK7下的48M一般够了,也可以把它设大点,反正内存多。
 
2.5 GC日志
1.基本配置
-Xloggc:/dev/shm/gc-myapplication.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails
详见JVM实用参数(八)GC日志,有人担心写GC日志会影响性能,但测试下来实在没什么影响,还是留一份用来排查好。
到后来,又发现如果遇上高IO的情况,如果GC的时候,操作系统正在flush pageCache 到磁盘,也可能导致GC log文件被锁住,从而让GC结束不了。所以把它指向了/dev/shm 这种内存中文件系统,避免这种停顿,详见Eliminating Large JVM GC Pauses Caused by Background IO Traffic
用+PrintGCDateStamps而不是PrintGCTimeStamps,打印可读的日期而不是时间戳。

2. -XX:+PrintGCApplicationStoppedTime,它的名字没起好,它除了打印清晰的GC停顿时间外,还可以打印其他的停顿时间,比如取消偏向锁,class 被agent redefine,code deoptimization等等,有助于发现一些原来没想到的问题,建议也加上。如果真的发现了一些不知什么的停顿,再临时加上"-XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1" 找原因。

3. GC日志默认会在重启后清空,但有人担心长期运行不重启的应用会把文件弄得很大,有"-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1M"的参数可以让日志滚动起来。但重启后的文件名太混乱太让人头痛,所以还是不加。
 
3. 监控篇
JVM输出的各种日志,如果未指定路径,通常会生成到运行应用的相同目录,为了避免有时候在不同的地方执行启动脚本,一般将日志路径集中设到一个固定的地方。
3.1 -XX:+PrintCommandLineFlags
运维有时会对启动参数做一些临时的更改,将每次启动的参数输出到stdout,将来有据可查。
打印出来的是命令行里设置了的参数以及因为这些参数隐式影响的参数,比如开了CMS后,-XX:+UseParNewGC也被自动打开。
3.2 -XX:-OmitStackTraceInFastThrow
为异常设置StackTrace是个昂贵的操作,所以当应用在相同地方抛出相同的异常N次(两万?)之后,JVM会对某些特定异常如NPE,数组越界等进行优化,不再带上异常栈。此时,你可能会看到日志里一条条Nul Point Exception,而真正输出完整栈的日志早被滚动到不知哪里去了,也就完全不知道这NPE发生在什么地方,欲哭无泪。 所以,将它禁止吧。
3.3 coredump与 -XX:ErrorFile
JVM crash时,hotspot 会生成一个error文件,提供JVM状态信息的细节。如前所述,将其输出到固定目录,避免到时会到处找这文件。文件名中的%p会被自动替换为应用的PID
-XX:ErrorFile=${MYLOGDIR}/hs_err_%p.log
当然,更好的做法是生成coredump,从CoreDump能够转出Heap Dump 和 Thread Dump 还有crash的地方,非常实用。
在启动脚本里加上 ulimit -c unlimited或其他的设置方式,如果有root权限,设一下输出目录更好
echo "/{MYLOGDIR}/coredump.%p" > /proc/sys/kernel/core_pattern
什么?你不知道这coredump有什么用?看来你是没遇过JVM Segment Fault的幸福人。
 
3.4 -XX:+HeapDumpOnOutOfMemoryError
在Out Of Memory,JVM快死快死掉的时候,输出Heap Dump到指定文件。不然开发很多时候还真不知道怎么重现错误。
路径只指向目录,JVM会保持文件名的唯一性,叫java_pid${pid}.hprof。如果指向文件,而文件已存在,反而不能写入。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOGDIR}/

3.5 JMX
-Dcom.sun.management.jmxremote.port=${MY_JMX_PORT} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1
以上设置,只让本地的Zabbix之类监控软件通过JMX监控JVM,不允许远程访问。
老规矩,文章里的链接,全部点“阅读原文”再看。
祝大家生产环境里的JVM都稳健无比,永远没bug。

Java性能优化指南1.8版,及唯品会的实战

admin 发表了文章 • 0 个评论 • 283 次浏览 • 2017-01-17 10:21 • 来自相关话题

原文链接:http://calvin1978.blogcn.com/a ... .html

一份平民化的应用性能优化CheckList

admin 发表了文章 • 0 个评论 • 163 次浏览 • 2017-01-17 10:17 • 来自相关话题

作为一份比较low的check list,高大上的话不多说了,直接开始。
1.总原则
一些正确但稍显废话的原则,但能指导后面每个章节的优化,所以还是要啰嗦一次。
可扩展性架构,堆机器能不能解决问题是最最优先考虑的问题
去中心化的点对点通信,优于通过中心代理的通信
池化的长连接,优于短连接
二进制数据,优于文本数据
尽量减少交互,一次调用的粗粒度聚合接口 优于 多次调用的细粒度接口
尽量减少交互,批量接口 优于 循环调用
尽量只交互必要的数据
尽量就近访问
尽量使用缓存
总是设定超时
在合适的场景,并行化执行
在合适的场景,异步化执行
2.环境准备
保证符合自家各种规范(没有的话赶紧回家写一个),尤其线下压测服务器的配置要与生产环境一致。
2.1 操作系统
自家规范调优应包含TCP内核参数,网卡参数及多队列绑定,IO&swap内核参数,ulimit资源限制等。
2.2 JVM与应用服务器
使用JDK7.0 u80 或 JDK8 最新版。
检查JVM启动参数已按自家规范调优,见《关键业务系统的JVM参数推荐》
检查应用服务器(Tomcat或微服务容器) 已按自家指南调优,如线程数等。

2.3 周边依赖系统
检查数据库,缓存,消息系统,已按自家指南调优。

2.4 后台辅助程序
检查日志收集,系统监控等,已使用最新版本,最优配置。
最好其最大消耗已被控制(通过cgroup,taskset等方式)。

2.5 测试程
压测工具如JMeter,启动参数要参考真实客户端优化(如JVM参数,Netty参数等)。
测试脚本和客户端程序经过review,不存在影响性能的步骤,不存在System.out.println()等明显的瓶颈。

2.6 流量模型
扇入模型:平时与高峰期的流量估算,各接口的流量比例,响应时间要求
扇出模型:各接口对远程服务、数据库、缓存、消息系统的调用比例,响应时间估算,缓存的命中率,击穿时的访问模式。
行文到此,大家大概可以感受到这份checklist的风格,都是大家明白的道理,但可能一时也会忘掉的,这里啰啰嗦嗦的給写下来。

3.数据库
仅以MySQL举例,特别鸣谢,我司DBA,如聂超大侠,文中大量观点均来自他们。
3.1 拓扑
根据扩展性原则考虑:
垂直拆分:按业务将表拆分到不同的库。
水平拆分:分表分库。
读写分离:在业务允许的情况下,在从库读取非实时数据。

3.2 Schema
自家规范应包含:
统一的存储引擎,主键策略。
禁用存储过程,函数,触发器,外键约束。

列类型永远越短越好,建议:布尔/枚举:tinyint,日期与时间戳:timestamp或int,char/text/blob: 尽量用符合实际长度的varchar(n),小数及货币:移位转为int 或 decimal,IP地址:int。

索引策略:索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面,合理创建联合索引,避免冗余索引,合理利用覆盖索引等。
3.3  SQL
1.  自家规范应包含:
禁止多于3表的join,禁用子查询
禁止where子句中对字段施加函数使索引失效,如to_date(add_time)>xxxxx
不建议使用%前缀模糊查询影响索引使用,模糊查询较多时建议使用ElasticSearch
避免隐式类型转化,如ISENDED=1 与 ISENDED='1'
  根据尽量少数据原则与尽量少交互的原则来设计SQL:
禁止select *
复杂度合理的SQL语句,减少交互次数。
  根据扩展性原则,将负载放在更容易伸缩的应用服务实例上:
尽量不要做数学运算,函数运算, 或者输出格式转换等非必要操作
避免count(*),计数统计实时要求较强使用memcache或者redis,非实时统计使用定时更新的单独统计表。
甚至排序都是不鼓励的,尽量在应用侧进行。另外避免多余的排序,使用GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用order by null。

2. 联系DBA进行MySQL统计的慢查询的Review,解析SQL查询计划时尽量避免extra列出现:Using File Sort,Using Temporary

3.4 DAO框架
根据尽量少交互与尽量少数据的原则,需使用对SQL完全可控的DAO框架,建议为MyBatis 或 Spring JDBC Template。
必须使用prepareStatement,提升性能与防注入。 
根据一切皆有超时的原则,配置SQL执行的超时。可在连接池里设置default值,可在MyBatis的Mapper定义里设置每个请求的超时,可惜规范是秒级的。
JDBC driver 规范本身不支持异步模式,如果一定要异步,可以像Quasar那样把请求封装成Callable交给另外的线程池执行,但注意其额外开销。
3.5 事务
不使用事务,连接池设置autocommit,使用其他方式来保持数据一致性。
通过@Transaction控制事务,事务跨度尽量短,把非事务范围内的业务逻辑剔除到被标注的函数之外。

只读事务可以不加事务标注。
3.6 连接池
选型:
在水平分库分表时,根据点对点通信优先的原则,尽量使用客户端分片的实现。功能不满足时才用MyCat中央代理。
推荐使用性能最高HikariCP,或者Druid,或者Tomcat JDBC,不推荐c3p0与DBCP。
连接池的配置:
配置连接初始值,再联系DBA获得线上数据库支持的连接数,计算最大连接数。
连接有效性的检查,只在连接空闲检测时执行,不在获取和归还连接时执行。直接使用数据库的Ping方案,不要配置检查的SQL。
根据总是设置超时的原则,配置获取连接的超时时间。
配置合理的空闲连接回收间隔和空闲时间。
 
4.缓存
4.1 多级缓存
根据缓存原则, 缓存 > 数据库/远程调用
根据就近原则, 堆内缓存 > 堆外缓存 > 集中式缓存
堆内缓存受大小限制,并影响GC
堆内缓存与堆外缓存,分布在每一台应用服务器上,所以刷新方式比集中式的复杂
堆外缓存与集中式缓存,需要序列化/反序列化对象
集中式缓存,有网络传输的成本,特别是数据超过一个网络包的大小。如果一次获取多个键时,在有分区的情况下,更需要收发多个网络包。
使用上述条件选择合适的缓存方案,或同时使用多级缓存,逐层回源。
4.2 公共
需要对回源进行并发控制,当key失效时,只有单一线程对该key回源。
基于二进制优于文本数据的原则,JSON的序列化方案较通用与更高的可读性。
而较大,结构较复杂的对象,基于Kyro,PB,Thrift的二进制序列化方案的性能更高,见后面的序列化方案部分。
4.3 堆内缓存
选型:
推荐Guava Cache。
Ehcache较重,性能也较差。更不要使用存在严重并发bug的Jodd Cache。
GuavaCache:
正确设置并行度等参数。
重载load()函数,实现单一线程回源。
Guava Cache能在后台刷新,在刷新的过程中,依然使用旧数据响应请求,不会造成卡顿,但需要重载实现reload()函数。
Guava Cache同时还支持并发安全版的WeakHashMap。
4.4 堆外缓存
选型:
推荐Cassandra的OHC 或者 OpenHFT的Chronical map2。
OHC够简单,其实R大不喜欢Chronical,玩的太深,换个JDK都可能跑不起来。
Chronical map3的license则较不友好,复杂度高且要求JDK8。
其他如Ehcache的Terracota Offheap 一向不喜欢。
4.5 Memcached
客户端:
基于点对点通信优于网关的原则,使用客户端一致性哈希分区。
推荐Spymemcached。 XMemcached 太久没更新,Folsom知名度不高。
注意Spymemcached为单线程单连接架构(一个SpyClient只有一条IO线程,与每个Memcached只有一条连接),必要时可多建几个SpyClient随机选择,但不要用Commons Pool去封装它,把Spy原本的设计一笔抹杀。
根据在合适场景使用并发的原则,Spymemcached支持异步API。
根据一切皆设超时的原则,可在ConnectionFactory中设置最大超时数,默认值两秒半太长。
数据结构:
Key必须设置失效时间。
Key必须有长度限制。
Value长度需要控制,以不超过1个网络包(MTU 1500byte)为佳。
Value大小差别较大的缓存类型,建议拆分到不同MC集群,否则会造成低使用率并且产生踢出。
4.6 Redis as Cache
Redis拓扑,基于点对点通信优于网关的原则:
无HA的普通分片:由Jedis客户端完成分片路由。
Redis Cluster:同样由Jedis客户端封装分区,跳转,重试等逻辑。最好使用最新的Jedis版本,旧版太多bug。
服务端:
Cache节点与持久化数据节点不要混用。
Cache节点是否需要持久化要衡量。
对热键进行监控,发现不合理的热健要进行分拆等处理。
客户端:
Jedis基于Apache Commons Pool进行了多连接的封装,正确配置总连接数不超过Redis Server的允许连接数。
性能考虑,空闲连接检查不要过于频繁(建议30秒以上),另不要打开testOnBorrow等测试参数。
根据一切皆有超时的原则,设定统一的调用超时(默认2秒),获取连接的最长等待时间参数,重试次数。
根据在合适的地方异步的原则,Jedis本身没有异步API,只在PipleLine模式下支持。
数据结构:
Key必须设置失效时间。
Key必须有长度限制。
Value长度需要控制,以不超过1个网络包(MTU 1500byte)为佳。另外set和sorted的elements不要超过5000个。
除了使用序列化的String,可以考虑用Hash来存储对象,注意内部结构为ZipList与HashTable时,hmget 与hgetall的不同复杂度。
命令:
慎用的命令:LANGE(0, -1), HGETALL, SMEMBER
高复杂度的命令: ZINTERSTORE, SINTERSTORE, ZUNIONSTORE, ZREM
尽量使用多参数的命令:MGET/MSET,HMGET/HMSET, LPUSH/RPUSH等
根据减少交互的原则,尽量使用pipeline
根据减少交互的原则,必要时可使用Redis的Lua脚本

5.服务调用
5.1 接口设计
1. 尽量少交互的原则:
支持批量接口,最大的批量,综合考虑调用者的需求与 后端存储的能力。
支持粗粒度接口,在支持原子细粒度接口的同时,支持粗粒度接口/聚合层接口,将多个数据源的获取,多个动作,合并成一个粗粒度接口。
2. 尽量少数据的原则:
在提供返回所有数据的大接口的同时,提供只提供满足部分调用者需要的轻量接口。
最好再提供能定制返回字段的接口。
3. 二进制数据优于文本数据
同样是一个简单通用性,与性能的选择,特别是大数据量时。
5.2 RESTful
仅以Apache HttpClient为例,大部分Restful框架都是对Apache HttpClient的封装。
另外OkHttp也值得看看。
不要重复创建ApacheClient实例,使用连接池,正确配置连接池的连接数。
连接池总是有锁,针对不同的服务,使用不同的Apache HttpClient实例,将锁分散开来。在高并发时比使用全局单例的ApacheClient,有很大的性能提升。
根据一切调用皆有超时的原则,每次调用均设置超时时间。RequestConfig里共有Connect Timeout, Socket Timout 和 从Pool中获取连接的Timeout三种超时。
需要异步或并行的场景,使用Apache AsyncHttpClient项目。但要注意AsyncHttpClient项目,检查调用超时的周期默认为1秒。
5.3 自家RPC框架
每家的RPC框架特性不同,但考虑点都类似。

6.消息异步
6.1 选型
根据就近原则,可以先尝试用JVM内的队列来解决,然后再考虑中央消息系统。
可靠性要求极高的选择RabbitMQ,可支持单条消息确认。
海量消息场景,允许极端情况下少量丢失则使用Kafka。
6.2 Kafka
在同步和异步之间做好权衡,异步批量发送可以极大的提高发送的速度。
关注消费者如下参数:commitInterval(自动提交offset间隔),prefetchSize(指单次从服务器批量拉取消息的大小),过大和过小都会影响性能,建议保持默认。
6.3 RabbitMQ
根据扩展性原则,RabbitMQ本身没有分片功能,但可以在客户端自行分片。
如非必要情况,应该保持默认的同步发送模式。
关注消费者如下参数:autocommit(自动提交确认,默认false) ,在消息拉取到本地即认为消费成功,而不是真正消费成功后提交。prefetchCount(预取消息条数,默认64条)
生产者在必要时也可以临时降级不进行confirm。

7. 日志
7.1 综述
Log4j2或logback,不要再使用Log4j。
除了应用启停日志,不允许使用超慢的System.out.println() 或 e.printStack();
严格控制日志量避免过高IO,对海量日志,应该有开关可以动态关停。
如果可能出现海量异常信息,可仿效JDK的优化,用RateLimiter进行限流,丢弃过多的异常日志。
7.2 内容
严格控制日志格式,避免出现消耗较大的输出如类名,方法名,行号等。
业务日志不要滥用toJSONString()来打印对象,尽量使用对象自身的toString()函数,因为JSON转换的消耗并不低。
在生产环境必定输出的日志,不要使用logger.info("hello {}", name)的模式,而是使用正确估算大小的StringBuilder直接拼装输出信息。
7.3 异步日志
同步日志的堵塞非常严重,特别是发生IO的时候,因此尽量使用异步日志。
Logback的异步方案存在一定问题,需要正确配置Queue长度,阀值达到多少时丢弃Warn以下的日志,最新版还可以设置如果队列已满,是等待还是直接丢弃日志。
如果觉得Logback的异步日志每次插入都要询问队列容量太过消耗,可重写一个直接入列,不成功则直接丢弃的版本。
8. 工具类
8.1 JSON
使用Jackson 或 FastJSON。GSON的性能较前两者为差,尤其是大对象时。
超大对象可以使用Jackson或FastJSON的Streaming API进行处理。
将不需要序列化的属性,通过Annotation排除掉。

FastJson:
尽量使用最新的版本。
SerializerFeature.DisableCircularReferenceDetect 关闭循环引用检查。
Jackson:
设置参数,不序列化为Null的属性,等于默认值的属性。
除了jackson-databinding,可试用简化版没那么多花样的jackon-jr。
8.2 二进制序列化
需要定义IDL的PB与Thrift,不需要定义的Storm等用的Kyro 都可选择,其他一些比较旧
就算了。
8.3 Bean复制
在VO,BO之间复制时,使用Orika(生成代码) 或 Dozer(缓存反射),不要使用需要每次进行反射的Apache BeanUitls,Spring BeanUtils。
8.4 日期
JDK的日期类与字符串之间的转换很慢且非线程安全。
继续用Date不想大动作的,就用Commons Lang3的FastDateFormat。
能大动作就用joda time的Date,或者JDK8的新日期API。

9.Java代码优化 与 业务逻辑优化
参考《Java调优指南1.8版》,对内存使用,并发与锁等方面进行优化。
规则前置,将消耗较大的操作放后面,如果前面的条件不满足时可。
另外前面提到的一堆原则,比如尽量缓存,尽量少交互,尽量少数据,并行,异步等,都可在此使用。

之前的打赏已经花完了,被迫继续卖文赚零花,各位看官再赏两块五呗。 查看全部
作为一份比较low的check list,高大上的话不多说了,直接开始。
1.总原则
一些正确但稍显废话的原则,但能指导后面每个章节的优化,所以还是要啰嗦一次。
可扩展性架构,堆机器能不能解决问题是最最优先考虑的问题
去中心化的点对点通信,优于通过中心代理的通信
池化的长连接,优于短连接
二进制数据,优于文本数据
尽量减少交互,一次调用的粗粒度聚合接口 优于 多次调用的细粒度接口
尽量减少交互,批量接口 优于 循环调用
尽量只交互必要的数据
尽量就近访问
尽量使用缓存
总是设定超时
在合适的场景,并行化执行
在合适的场景,异步化执行
2.环境准备
保证符合自家各种规范(没有的话赶紧回家写一个),尤其线下压测服务器的配置要与生产环境一致。
2.1 操作系统
自家规范调优应包含TCP内核参数,网卡参数及多队列绑定,IO&swap内核参数,ulimit资源限制等。
2.2 JVM与应用服务器
使用JDK7.0 u80 或 JDK8 最新版。
检查JVM启动参数已按自家规范调优,见《关键业务系统的JVM参数推荐》
检查应用服务器(Tomcat或微服务容器) 已按自家指南调优,如线程数等。

2.3 周边依赖系统
检查数据库,缓存,消息系统,已按自家指南调优。

2.4 后台辅助程序
检查日志收集,系统监控等,已使用最新版本,最优配置。
最好其最大消耗已被控制(通过cgroup,taskset等方式)。

2.5 测试程
压测工具如JMeter,启动参数要参考真实客户端优化(如JVM参数,Netty参数等)。
测试脚本和客户端程序经过review,不存在影响性能的步骤,不存在System.out.println()等明显的瓶颈。

2.6 流量模型
扇入模型:平时与高峰期的流量估算,各接口的流量比例,响应时间要求
扇出模型:各接口对远程服务、数据库、缓存、消息系统的调用比例,响应时间估算,缓存的命中率,击穿时的访问模式。
行文到此,大家大概可以感受到这份checklist的风格,都是大家明白的道理,但可能一时也会忘掉的,这里啰啰嗦嗦的給写下来。

3.数据库
仅以MySQL举例,特别鸣谢,我司DBA,如聂超大侠,文中大量观点均来自他们。
3.1 拓扑
根据扩展性原则考虑:
垂直拆分:按业务将表拆分到不同的库。
水平拆分:分表分库。
读写分离:在业务允许的情况下,在从库读取非实时数据。

3.2 Schema
自家规范应包含:
统一的存储引擎,主键策略。
禁用存储过程,函数,触发器,外键约束。

列类型永远越短越好,建议:布尔/枚举:tinyint,日期与时间戳:timestamp或int,char/text/blob: 尽量用符合实际长度的varchar(n),小数及货币:移位转为int 或 decimal,IP地址:int。

索引策略:索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面,合理创建联合索引,避免冗余索引,合理利用覆盖索引等。
3.3  SQL
1.  自家规范应包含:
禁止多于3表的join,禁用子查询
禁止where子句中对字段施加函数使索引失效,如to_date(add_time)>xxxxx
不建议使用%前缀模糊查询影响索引使用,模糊查询较多时建议使用ElasticSearch
避免隐式类型转化,如ISENDED=1 与 ISENDED='1'
  根据尽量少数据原则与尽量少交互的原则来设计SQL:
禁止select *
复杂度合理的SQL语句,减少交互次数。
  根据扩展性原则,将负载放在更容易伸缩的应用服务实例上:
尽量不要做数学运算,函数运算, 或者输出格式转换等非必要操作
避免count(*),计数统计实时要求较强使用memcache或者redis,非实时统计使用定时更新的单独统计表。
甚至排序都是不鼓励的,尽量在应用侧进行。另外避免多余的排序,使用GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用order by null。

2. 联系DBA进行MySQL统计的慢查询的Review,解析SQL查询计划时尽量避免extra列出现:Using File Sort,Using Temporary

3.4 DAO框架
根据尽量少交互与尽量少数据的原则,需使用对SQL完全可控的DAO框架,建议为MyBatis 或 Spring JDBC Template。
必须使用prepareStatement,提升性能与防注入。 
根据一切皆有超时的原则,配置SQL执行的超时。可在连接池里设置default值,可在MyBatis的Mapper定义里设置每个请求的超时,可惜规范是秒级的。
JDBC driver 规范本身不支持异步模式,如果一定要异步,可以像Quasar那样把请求封装成Callable交给另外的线程池执行,但注意其额外开销。
3.5 事务
不使用事务,连接池设置autocommit,使用其他方式来保持数据一致性。
通过@Transaction控制事务,事务跨度尽量短,把非事务范围内的业务逻辑剔除到被标注的函数之外。

只读事务可以不加事务标注。
3.6 连接池
选型:
在水平分库分表时,根据点对点通信优先的原则,尽量使用客户端分片的实现。功能不满足时才用MyCat中央代理。
推荐使用性能最高HikariCP,或者Druid,或者Tomcat JDBC,不推荐c3p0与DBCP。
连接池的配置:
配置连接初始值,再联系DBA获得线上数据库支持的连接数,计算最大连接数。
连接有效性的检查,只在连接空闲检测时执行,不在获取和归还连接时执行。直接使用数据库的Ping方案,不要配置检查的SQL。
根据总是设置超时的原则,配置获取连接的超时时间。
配置合理的空闲连接回收间隔和空闲时间。
 
4.缓存
4.1 多级缓存
根据缓存原则, 缓存 > 数据库/远程调用
根据就近原则, 堆内缓存 > 堆外缓存 > 集中式缓存
堆内缓存受大小限制,并影响GC
堆内缓存与堆外缓存,分布在每一台应用服务器上,所以刷新方式比集中式的复杂
堆外缓存与集中式缓存,需要序列化/反序列化对象
集中式缓存,有网络传输的成本,特别是数据超过一个网络包的大小。如果一次获取多个键时,在有分区的情况下,更需要收发多个网络包。
使用上述条件选择合适的缓存方案,或同时使用多级缓存,逐层回源。
4.2 公共
需要对回源进行并发控制,当key失效时,只有单一线程对该key回源。
基于二进制优于文本数据的原则,JSON的序列化方案较通用与更高的可读性。
而较大,结构较复杂的对象,基于Kyro,PB,Thrift的二进制序列化方案的性能更高,见后面的序列化方案部分。
4.3 堆内缓存
选型:
推荐Guava Cache。
Ehcache较重,性能也较差。更不要使用存在严重并发bug的Jodd Cache。
GuavaCache:
正确设置并行度等参数。
重载load()函数,实现单一线程回源。
Guava Cache能在后台刷新,在刷新的过程中,依然使用旧数据响应请求,不会造成卡顿,但需要重载实现reload()函数。
Guava Cache同时还支持并发安全版的WeakHashMap。
4.4 堆外缓存
选型:
推荐Cassandra的OHC 或者 OpenHFT的Chronical map2。
OHC够简单,其实R大不喜欢Chronical,玩的太深,换个JDK都可能跑不起来。
Chronical map3的license则较不友好,复杂度高且要求JDK8。
其他如Ehcache的Terracota Offheap 一向不喜欢。
4.5 Memcached
客户端:
基于点对点通信优于网关的原则,使用客户端一致性哈希分区。
推荐Spymemcached。 XMemcached 太久没更新,Folsom知名度不高。
注意Spymemcached为单线程单连接架构(一个SpyClient只有一条IO线程,与每个Memcached只有一条连接),必要时可多建几个SpyClient随机选择,但不要用Commons Pool去封装它,把Spy原本的设计一笔抹杀。
根据在合适场景使用并发的原则,Spymemcached支持异步API。
根据一切皆设超时的原则,可在ConnectionFactory中设置最大超时数,默认值两秒半太长。
数据结构:
Key必须设置失效时间。
Key必须有长度限制。
Value长度需要控制,以不超过1个网络包(MTU 1500byte)为佳。
Value大小差别较大的缓存类型,建议拆分到不同MC集群,否则会造成低使用率并且产生踢出。
4.6 Redis as Cache
Redis拓扑,基于点对点通信优于网关的原则:
无HA的普通分片:由Jedis客户端完成分片路由。
Redis Cluster:同样由Jedis客户端封装分区,跳转,重试等逻辑。最好使用最新的Jedis版本,旧版太多bug。
服务端:
Cache节点与持久化数据节点不要混用。
Cache节点是否需要持久化要衡量。
对热键进行监控,发现不合理的热健要进行分拆等处理。
客户端:
Jedis基于Apache Commons Pool进行了多连接的封装,正确配置总连接数不超过Redis Server的允许连接数。
性能考虑,空闲连接检查不要过于频繁(建议30秒以上),另不要打开testOnBorrow等测试参数。
根据一切皆有超时的原则,设定统一的调用超时(默认2秒),获取连接的最长等待时间参数,重试次数。
根据在合适的地方异步的原则,Jedis本身没有异步API,只在PipleLine模式下支持。
数据结构:
Key必须设置失效时间。
Key必须有长度限制。
Value长度需要控制,以不超过1个网络包(MTU 1500byte)为佳。另外set和sorted的elements不要超过5000个。
除了使用序列化的String,可以考虑用Hash来存储对象,注意内部结构为ZipList与HashTable时,hmget 与hgetall的不同复杂度。
命令:
慎用的命令:LANGE(0, -1), HGETALL, SMEMBER
高复杂度的命令: ZINTERSTORE, SINTERSTORE, ZUNIONSTORE, ZREM
尽量使用多参数的命令:MGET/MSET,HMGET/HMSET, LPUSH/RPUSH等
根据减少交互的原则,尽量使用pipeline
根据减少交互的原则,必要时可使用Redis的Lua脚本

5.服务调用
5.1 接口设计
1. 尽量少交互的原则:
支持批量接口,最大的批量,综合考虑调用者的需求与 后端存储的能力。
支持粗粒度接口,在支持原子细粒度接口的同时,支持粗粒度接口/聚合层接口,将多个数据源的获取,多个动作,合并成一个粗粒度接口。
2. 尽量少数据的原则:
在提供返回所有数据的大接口的同时,提供只提供满足部分调用者需要的轻量接口。
最好再提供能定制返回字段的接口。
3. 二进制数据优于文本数据
同样是一个简单通用性,与性能的选择,特别是大数据量时。
5.2 RESTful
仅以Apache HttpClient为例,大部分Restful框架都是对Apache HttpClient的封装。
另外OkHttp也值得看看。
不要重复创建ApacheClient实例,使用连接池,正确配置连接池的连接数。
连接池总是有锁,针对不同的服务,使用不同的Apache HttpClient实例,将锁分散开来。在高并发时比使用全局单例的ApacheClient,有很大的性能提升。
根据一切调用皆有超时的原则,每次调用均设置超时时间。RequestConfig里共有Connect Timeout, Socket Timout 和 从Pool中获取连接的Timeout三种超时。
需要异步或并行的场景,使用Apache AsyncHttpClient项目。但要注意AsyncHttpClient项目,检查调用超时的周期默认为1秒。
5.3 自家RPC框架
每家的RPC框架特性不同,但考虑点都类似。

6.消息异步
6.1 选型
根据就近原则,可以先尝试用JVM内的队列来解决,然后再考虑中央消息系统。
可靠性要求极高的选择RabbitMQ,可支持单条消息确认。
海量消息场景,允许极端情况下少量丢失则使用Kafka。
6.2 Kafka
在同步和异步之间做好权衡,异步批量发送可以极大的提高发送的速度。
关注消费者如下参数:commitInterval(自动提交offset间隔),prefetchSize(指单次从服务器批量拉取消息的大小),过大和过小都会影响性能,建议保持默认。
6.3 RabbitMQ
根据扩展性原则,RabbitMQ本身没有分片功能,但可以在客户端自行分片。
如非必要情况,应该保持默认的同步发送模式。
关注消费者如下参数:autocommit(自动提交确认,默认false) ,在消息拉取到本地即认为消费成功,而不是真正消费成功后提交。prefetchCount(预取消息条数,默认64条)
生产者在必要时也可以临时降级不进行confirm。

7. 日志
7.1 综述
Log4j2或logback,不要再使用Log4j。
除了应用启停日志,不允许使用超慢的System.out.println() 或 e.printStack();
严格控制日志量避免过高IO,对海量日志,应该有开关可以动态关停。
如果可能出现海量异常信息,可仿效JDK的优化,用RateLimiter进行限流,丢弃过多的异常日志。
7.2 内容
严格控制日志格式,避免出现消耗较大的输出如类名,方法名,行号等。
业务日志不要滥用toJSONString()来打印对象,尽量使用对象自身的toString()函数,因为JSON转换的消耗并不低。
在生产环境必定输出的日志,不要使用logger.info("hello {}", name)的模式,而是使用正确估算大小的StringBuilder直接拼装输出信息。
7.3 异步日志
同步日志的堵塞非常严重,特别是发生IO的时候,因此尽量使用异步日志。
Logback的异步方案存在一定问题,需要正确配置Queue长度,阀值达到多少时丢弃Warn以下的日志,最新版还可以设置如果队列已满,是等待还是直接丢弃日志。
如果觉得Logback的异步日志每次插入都要询问队列容量太过消耗,可重写一个直接入列,不成功则直接丢弃的版本。
8. 工具类
8.1 JSON
使用Jackson 或 FastJSON。GSON的性能较前两者为差,尤其是大对象时。
超大对象可以使用Jackson或FastJSON的Streaming API进行处理。
将不需要序列化的属性,通过Annotation排除掉。

FastJson:
尽量使用最新的版本。
SerializerFeature.DisableCircularReferenceDetect 关闭循环引用检查。
Jackson:
设置参数,不序列化为Null的属性,等于默认值的属性。
除了jackson-databinding,可试用简化版没那么多花样的jackon-jr。
8.2 二进制序列化
需要定义IDL的PB与Thrift,不需要定义的Storm等用的Kyro 都可选择,其他一些比较旧
就算了。
8.3 Bean复制
在VO,BO之间复制时,使用Orika(生成代码) 或 Dozer(缓存反射),不要使用需要每次进行反射的Apache BeanUitls,Spring BeanUtils。
8.4 日期
JDK的日期类与字符串之间的转换很慢且非线程安全。
继续用Date不想大动作的,就用Commons Lang3的FastDateFormat。
能大动作就用joda time的Date,或者JDK8的新日期API。

9.Java代码优化 与 业务逻辑优化
参考《Java调优指南1.8版》,对内存使用,并发与锁等方面进行优化。
规则前置,将消耗较大的操作放后面,如果前面的条件不满足时可。
另外前面提到的一堆原则,比如尽量缓存,尽量少交互,尽量少数据,并行,异步等,都可在此使用。

之前的打赏已经花完了,被迫继续卖文赚零花,各位看官再赏两块五呗。

努力错了方向,你还觉得全世界都辜负了你?

admin 发表了文章 • 0 个评论 • 107 次浏览 • 2016-12-30 14:02 • 来自相关话题

“梦想”是一个容易被妖魔化的词语。毕竟无论选秀节目还是鸡汤故事,除了悲惨的遭遇和“致贱人致low逼”,没什么比“梦想”更好用了。


 

似乎没有梦想都不好意思出来混,和人聊天时总觉得没有底气。

 

可拥有梦想固然容易,实现不了却令人焦虑。

 

如果可以,我也希望自己能变成比尔·盖茨啊。只不过,当能力实在支撑不起梦想的时候,我们该何去何从?《奇葩说》第二季的冠军邱晨说:

 

“换一个梦想咯!”

 




这看似戏谑的一句话,其中也透露着些许生活的真理——梦想就像男朋友,难免遇到错的。

  

本文由LinkedIn原创,作者爱栗丝。




 

邱晨自称“没底线的辩手和不靠谱的记者,干不下去的编辑和半路出家的设计,没动力的创意人和穿拖鞋的管理者”。




她现在的另一个身份,是米果文化联合创始人兼COO。

 

如果说持之以恒的努力才足以撑起梦想,一个工作换得这么频繁的人,为什么能取得这么大的成就?

 


1
没有梦想并不可耻

伪梦想换掉也罢







不久前,我问过一些朋友,如果你有花不完的钱,你还会工作吗?

 

95%的受访者回答——不会。

 

我又继续追问了这些受访者,不工作了你会选择做什么?

 

大多数人都愣住了,一时间答不上来。

 

不得不承认的是——许多人不过是为了生存而活着,并没有梦想,或者在长久地陷在眼前的细碎生活中忘了做梦。

 

没有梦想,是件值得羞耻的事情吗?

 

不。

 

比起那些打着梦想幌子绷人设的人,勇敢承认自己没有梦想,倒要坦荡得多。

 

互联网时代给了年轻人充分的机会,我们听过不少年少成名的故事。

 

90后的余佳文站在《开讲啦》的舞台上,告诉大家他要拿出公司收入的一亿元来分给员工,梦比天高。

 

98年出生的王凯歆,18岁就创办了“个性化”电商平台神奇百货,在它并不神奇的商业模式没有公布于众之前,也曾是媒体追逐的天才少女。










这些新闻不断告诉我们,那些年纪比你小、学历比你低、长相不及你的人,已经那么厉害了,而你还挤在巴掌大的出租屋里,扼腕叹息在最好的青春里奉献了自己全部的精力,还付不起北上广一套100尺的房。

 

于是,不少年轻人心想,自己真是个辣鸡啊,什么时候才能像他们一样实现梦想啊?

 

等等,你羡慕这些创业成功的人,是因为他们实现了自己的梦想?

 

别骗自己了,你想要的不过是像他们一样“成功”而已。这种虚无缥缈终有一朝会梦碎的“成功”有另外一个名字——名利双收。

 

财务自由,是许多人安全感的来源,追求更好生活的手段。追求名利也没错,但拜托,请别打上梦想的幌子。

 


2实现梦想的道路

总是布满荆棘







在访问的另外5%回答里,我听到了让人惊喜的答案,也听到了无奈和担忧。

 

有个朋友说她的梦想是做演员,可是演员这条路自己能决定的太少。

 

她演了5年,至今还是小角色。有一部戏,制片直接跟试镜演员说,你们谁能拉来5000万赞助就用谁。更过分的潜规则也比比皆是。她说,自己快坚持不下去了。

 

另一位朋友说想做服装设计师。他本科毕业后进了一家服装设计公司,可是设计总被领导改得面目全非。

 

想自己开工作室吧,前期基本是烧钱,他没这个资本。

 

这世界上拥有梦想的人是少数,有能力做梦的人就更少了。

 

坏的鸡汤告诉你,《当你的能力还撑不起梦想时,你就该努力》。

 

似乎只要坚持,梦想就一定可以实现。

 

但真的是这样吗?










最近,一项综合了30项元分析的研究发现,在体育竞技中,精深练习只能解释18%的差异。

 

研究者说,在其它诸如音乐、游戏、教育领域,都发现“一万小时天才理论”是不正确的——天赋、基因在这些领域所起的作用更重要。

 

这似乎是给了我们当头一棒。




承认吧,无论如何努力,有些梦想你用尽全部的力量都无法实现。

 

我不是劝你放弃梦想,但当你做了一切能做的还是陷在痛苦里,也许是时候换个方向了。

 


3梦想是一个动词

你要根据人生阅历去丰富它




 

梦想是一个动词,不仅代表你要努力去做,它也是随着人生阅历的丰富而动态变化的概念。

 

剥离自己的长处和手中握有的资源谈梦想,不是一件明智的事情。

 

如果你想做一个靠设计实现财务自由的服装设计师,但苦于资本匮乏和国内服装行业的缺乏生机,也许在内容创业的黄金时代,成为时尚博主是一个更好的选择。

 

这并不意味着你在奔向服装设计师的路上尝过的苦都白费了,这段试错反而会成为你重新尝试的宝贵资本。

 

当然,试错也要设定一个确切的时间线,毕竟试错的最终目的是找到正确的轨道。你不能一直错下去,对吧?

 

哈佛商学院毕业生Tara Hagan建立了一个人生最佳平衡点的模型,用来确定一个人是否走在正确的轨道上。

 

人生的最佳平衡点,即你所热爱的、你所擅长的和社会价值观的交集。





确保一个人是否处在正确的人生轨迹上,不是靠一个简单的清单列表,而是一系列明智决定的结果,更像是丰富多彩的经历日积月累串联起来的过程。

 

正如史蒂芬 · 乔布斯在斯坦福毕业演讲中提出的著名的点线言论:

 

“无论哪条路,如果在每个人生重要转折点:




选择新工作,搬到新处所,开始新的爱好,教门课程……




选择能给你更多活力、激发你的好奇心,以及也是你所擅长的、你所热爱的和社会认可三者的交集,那么我可以想象你在自己的人生轨迹上一路走好。”

 

所以,换一个梦想不是劝你轻易放弃梦想。




我是想告诉你,人生就像一场航行,不断调整方向才能避免触礁,扬帆远航。




毕竟,你又没和梦想结婚,为什么明知道不是Mr. Right还不换一个呢,对吧? 查看全部
“梦想”是一个容易被妖魔化的词语。毕竟无论选秀节目还是鸡汤故事,除了悲惨的遭遇和“致贱人致low逼”,没什么比“梦想”更好用了。


 

似乎没有梦想都不好意思出来混,和人聊天时总觉得没有底气。

 

可拥有梦想固然容易,实现不了却令人焦虑。

 

如果可以,我也希望自己能变成比尔·盖茨啊。只不过,当能力实在支撑不起梦想的时候,我们该何去何从?《奇葩说》第二季的冠军邱晨说:

 

“换一个梦想咯!”

 




这看似戏谑的一句话,其中也透露着些许生活的真理——梦想就像男朋友,难免遇到错的。

  

本文由LinkedIn原创,作者爱栗丝。




 

邱晨自称“没底线的辩手和不靠谱的记者,干不下去的编辑和半路出家的设计,没动力的创意人和穿拖鞋的管理者”。




她现在的另一个身份,是米果文化联合创始人兼COO。

 

如果说持之以恒的努力才足以撑起梦想,一个工作换得这么频繁的人,为什么能取得这么大的成就?

 


1
没有梦想并不可耻

伪梦想换掉也罢







不久前,我问过一些朋友,如果你有花不完的钱,你还会工作吗?

 

95%的受访者回答——不会。

 

我又继续追问了这些受访者,不工作了你会选择做什么?

 

大多数人都愣住了,一时间答不上来。

 

不得不承认的是——许多人不过是为了生存而活着,并没有梦想,或者在长久地陷在眼前的细碎生活中忘了做梦。

 

没有梦想,是件值得羞耻的事情吗?

 

不。

 

比起那些打着梦想幌子绷人设的人,勇敢承认自己没有梦想,倒要坦荡得多。

 

互联网时代给了年轻人充分的机会,我们听过不少年少成名的故事。

 

90后的余佳文站在《开讲啦》的舞台上,告诉大家他要拿出公司收入的一亿元来分给员工,梦比天高。

 

98年出生的王凯歆,18岁就创办了“个性化”电商平台神奇百货,在它并不神奇的商业模式没有公布于众之前,也曾是媒体追逐的天才少女。










这些新闻不断告诉我们,那些年纪比你小、学历比你低、长相不及你的人,已经那么厉害了,而你还挤在巴掌大的出租屋里,扼腕叹息在最好的青春里奉献了自己全部的精力,还付不起北上广一套100尺的房。

 

于是,不少年轻人心想,自己真是个辣鸡啊,什么时候才能像他们一样实现梦想啊?

 

等等,你羡慕这些创业成功的人,是因为他们实现了自己的梦想?

 

别骗自己了,你想要的不过是像他们一样“成功”而已。这种虚无缥缈终有一朝会梦碎的“成功”有另外一个名字——名利双收。

 

财务自由,是许多人安全感的来源,追求更好生活的手段。追求名利也没错,但拜托,请别打上梦想的幌子。

 


2实现梦想的道路

总是布满荆棘







在访问的另外5%回答里,我听到了让人惊喜的答案,也听到了无奈和担忧。

 

有个朋友说她的梦想是做演员,可是演员这条路自己能决定的太少。

 

她演了5年,至今还是小角色。有一部戏,制片直接跟试镜演员说,你们谁能拉来5000万赞助就用谁。更过分的潜规则也比比皆是。她说,自己快坚持不下去了。

 

另一位朋友说想做服装设计师。他本科毕业后进了一家服装设计公司,可是设计总被领导改得面目全非。

 

想自己开工作室吧,前期基本是烧钱,他没这个资本。

 

这世界上拥有梦想的人是少数,有能力做梦的人就更少了。

 

坏的鸡汤告诉你,《当你的能力还撑不起梦想时,你就该努力》。

 

似乎只要坚持,梦想就一定可以实现。

 

但真的是这样吗?










最近,一项综合了30项元分析的研究发现,在体育竞技中,精深练习只能解释18%的差异。

 

研究者说,在其它诸如音乐、游戏、教育领域,都发现“一万小时天才理论”是不正确的——天赋、基因在这些领域所起的作用更重要。

 

这似乎是给了我们当头一棒。




承认吧,无论如何努力,有些梦想你用尽全部的力量都无法实现。

 

我不是劝你放弃梦想,但当你做了一切能做的还是陷在痛苦里,也许是时候换个方向了。

 


3梦想是一个动词

你要根据人生阅历去丰富它




 

梦想是一个动词,不仅代表你要努力去做,它也是随着人生阅历的丰富而动态变化的概念。

 

剥离自己的长处和手中握有的资源谈梦想,不是一件明智的事情。

 

如果你想做一个靠设计实现财务自由的服装设计师,但苦于资本匮乏和国内服装行业的缺乏生机,也许在内容创业的黄金时代,成为时尚博主是一个更好的选择。

 

这并不意味着你在奔向服装设计师的路上尝过的苦都白费了,这段试错反而会成为你重新尝试的宝贵资本。

 

当然,试错也要设定一个确切的时间线,毕竟试错的最终目的是找到正确的轨道。你不能一直错下去,对吧?

 

哈佛商学院毕业生Tara Hagan建立了一个人生最佳平衡点的模型,用来确定一个人是否走在正确的轨道上。

 

人生的最佳平衡点,即你所热爱的、你所擅长的和社会价值观的交集。

640.jpg

确保一个人是否处在正确的人生轨迹上,不是靠一个简单的清单列表,而是一系列明智决定的结果,更像是丰富多彩的经历日积月累串联起来的过程。

 

正如史蒂芬 · 乔布斯在斯坦福毕业演讲中提出的著名的点线言论:

 

“无论哪条路,如果在每个人生重要转折点:




选择新工作,搬到新处所,开始新的爱好,教门课程……




选择能给你更多活力、激发你的好奇心,以及也是你所擅长的、你所热爱的和社会认可三者的交集,那么我可以想象你在自己的人生轨迹上一路走好。”

 

所以,换一个梦想不是劝你轻易放弃梦想。




我是想告诉你,人生就像一场航行,不断调整方向才能避免触礁,扬帆远航。




毕竟,你又没和梦想结婚,为什么明知道不是Mr. Right还不换一个呢,对吧?

怎么选择另一半?

admin 发表了文章 • 2 个评论 • 168 次浏览 • 2016-12-12 13:55 • 来自相关话题

    这是一篇教你选择最终的那个他或她的文章,而不以结婚目的选择男女友的就别看了,毕竟如果想选择‘炮友’(我至今很难体会这个词)之类的话,看看颜值和那方面功夫就好了。我不止一次的说过,爱情说透了,就是精神、物质、肉体三者的契合。三者契合度多高,决定了爱情的质量和持续性。

女人
    大多数不谙世事的女人,选择另一半会看感觉,所以但凡说到择偶“看感觉”的女人,基本上情感上的认知能力没有太高的段位,将来的感情幸福的真叫运气,未来大体会遭遇一段挫折。

    因为感觉来的容易,去的也容易,女人很可能因为男人的一个动作神态,一句话,一件小事而喜欢上,或者不喜欢上她。

    她们总结不出自己所喜欢的男子的特征,或者她们认为当这些条条框框列出后,也没用,最终那个人出现后都会改变——能改变的大部分你列出标准的女人,只能说明你其实在情感上不成熟。

    而另一些有较高认知层次的女孩,会对自己的择偶标准很清楚,譬如有多少财力,长得大致什么类型,性格大体怎么样等等。

男人

    同样对于男人也是,男人对女人的感觉简单通俗很多,大多数在于容貌,长得好看的女人,男人看到就会有感觉,至少下半身也会有感觉。长得好看的女子自然会吸引到很多男人的追求。

    但在情感认知能力上有较高段位的男人,同样不会饥不择食,除了外貌身材外,还会对女孩的声音、谈吐、打扮、气质、知识结构等诸多方面有要求。

    当这个男人跟不少外表姣好(90分+)的女孩、不同性格和类型的女人曾谈过恋爱后;有一天,会不再将女人的外表身材放在那么重要的位置——当然,我从不否认,女人的外表一定是加分的。

    而这个观点,在屌丝看来是比较难以理解的,屌丝心目中的女神,完全来自于颜值;他们很难想象一个阅尽女色的男人对于一个只是外貌长得好看,但没什么内涵,素质的女人,是连想“上她”的感觉都不会有。

    那些超越了优秀,达到了卓越的男人,他们会真正知道哪种类型的女人能跟自己长相守,陪伴自己渡过一生。
是的,那就是三观无限接近者。

    因为工作的关系,我能接触到极其庞大数量的单身男女,知道TA们的思想和行为特征;至少在上海,北京等一线城市,大多数男女们在择偶时,已经将“三观一致”放在了很重要的位置,而这在十年前,提的男女还比较少。这当然是一件好事。至少说明都市男女在择偶过程中进入理性的阶段。

    看到这里,很多人可能会说“三观一致”很空洞,具体是指哪些呢?

    好吧,告诉你:人生观、世界观、价值观就这三点

    再具体一点?
    对于朋友借钱的看法,你是怎么处理的?你的另一半又会怎么处理的?

    对待有亲朋好友从另一个城市到你所在的城市旅游,你会不会放弃工作时间去陪这些亲朋好友游玩?你的做法,跟你另一半的做法是否相同?

    你自己想过怎样的人生?譬如一个男人,如果他即使活到了不惑之年,也从来不认为自己是平凡人,所谓天降大任于斯人,是要干一番轰天动地的事情的,那么你的另一半如果只想过过小日子,小富即安的,那么你们也走不到一块。
……
    凡此种种,皆为三观。话说好像很少有文章将三观,能用一个个鲜活案例来进行阐述的。即使我上面仅仅列的三个,也很少有文章会列出。

    性格是凌驾于三观之下的,而床上的和谐甚至可以排在第二;至于一般认知较低者关心的什么兴趣爱好、生活习惯等则是可以培养的。而生活情趣自然也是非常重要的,可以列进前四;只是很多人没想过,生活情趣也取决于三观,两个三观一致的人,可能会有一些在外人看来很枯燥的生活,但TA们两人之间却觉得某件事情很有情趣。

    综上所述,对于情感质量要求较高的人;找一个拥有无限接近的三观伴侣是陪伴你一生的前提,此即为灵魂伴侣。

    而三观也分健全与否,思维也分完善与否。当你拥有70%健全三观的人,跟另一个90%健全三观的人,一开始在一起未必会匹配;如果那个70%健全三观的人没有短时间内得以成长,两人终将会有分歧。最理想的状态是:两个人的三观都接近于完善,对世间万物的看法,对看透事物的能力都趋于完善且定型。这样的三观一致,才是大道。

    那么从理论上讲,一个人一生可以遇到不止一个与你三观无限接近的人。但:
首先:即使一个各方面思维都很早熟的人,一个有极大智慧的人,当他三观趋于完善时,也往往已过而立之年。
其次:很可能下次遇到时,是十年后,对方未娶,你未嫁。结合上述那条,这个概率本不大。

     所以,你的人生中,一旦遇到一个三观与你无限接近的人,请务必珍惜与把握。她可能来的如此突然。 查看全部
    这是一篇教你选择最终的那个他或她的文章,而不以结婚目的选择男女友的就别看了,毕竟如果想选择‘炮友’(我至今很难体会这个词)之类的话,看看颜值和那方面功夫就好了。我不止一次的说过,爱情说透了,就是精神、物质、肉体三者的契合。三者契合度多高,决定了爱情的质量和持续性。

女人
    大多数不谙世事的女人,选择另一半会看感觉,所以但凡说到择偶“看感觉”的女人,基本上情感上的认知能力没有太高的段位,将来的感情幸福的真叫运气,未来大体会遭遇一段挫折。

    因为感觉来的容易,去的也容易,女人很可能因为男人的一个动作神态,一句话,一件小事而喜欢上,或者不喜欢上她。

    她们总结不出自己所喜欢的男子的特征,或者她们认为当这些条条框框列出后,也没用,最终那个人出现后都会改变——能改变的大部分你列出标准的女人,只能说明你其实在情感上不成熟。

    而另一些有较高认知层次的女孩,会对自己的择偶标准很清楚,譬如有多少财力,长得大致什么类型,性格大体怎么样等等。

男人

    同样对于男人也是,男人对女人的感觉简单通俗很多,大多数在于容貌,长得好看的女人,男人看到就会有感觉,至少下半身也会有感觉。长得好看的女子自然会吸引到很多男人的追求。

    但在情感认知能力上有较高段位的男人,同样不会饥不择食,除了外貌身材外,还会对女孩的声音、谈吐、打扮、气质、知识结构等诸多方面有要求。

    当这个男人跟不少外表姣好(90分+)的女孩、不同性格和类型的女人曾谈过恋爱后;有一天,会不再将女人的外表身材放在那么重要的位置——当然,我从不否认,女人的外表一定是加分的。

    而这个观点,在屌丝看来是比较难以理解的,屌丝心目中的女神,完全来自于颜值;他们很难想象一个阅尽女色的男人对于一个只是外貌长得好看,但没什么内涵,素质的女人,是连想“上她”的感觉都不会有。

    那些超越了优秀,达到了卓越的男人,他们会真正知道哪种类型的女人能跟自己长相守,陪伴自己渡过一生。
是的,那就是三观无限接近者。

    因为工作的关系,我能接触到极其庞大数量的单身男女,知道TA们的思想和行为特征;至少在上海,北京等一线城市,大多数男女们在择偶时,已经将“三观一致”放在了很重要的位置,而这在十年前,提的男女还比较少。这当然是一件好事。至少说明都市男女在择偶过程中进入理性的阶段。

    看到这里,很多人可能会说“三观一致”很空洞,具体是指哪些呢?

    好吧,告诉你:人生观、世界观、价值观就这三点

    再具体一点?
    对于朋友借钱的看法,你是怎么处理的?你的另一半又会怎么处理的?

    对待有亲朋好友从另一个城市到你所在的城市旅游,你会不会放弃工作时间去陪这些亲朋好友游玩?你的做法,跟你另一半的做法是否相同?

    你自己想过怎样的人生?譬如一个男人,如果他即使活到了不惑之年,也从来不认为自己是平凡人,所谓天降大任于斯人,是要干一番轰天动地的事情的,那么你的另一半如果只想过过小日子,小富即安的,那么你们也走不到一块。
……
    凡此种种,皆为三观。话说好像很少有文章将三观,能用一个个鲜活案例来进行阐述的。即使我上面仅仅列的三个,也很少有文章会列出。

    性格是凌驾于三观之下的,而床上的和谐甚至可以排在第二;至于一般认知较低者关心的什么兴趣爱好、生活习惯等则是可以培养的。而生活情趣自然也是非常重要的,可以列进前四;只是很多人没想过,生活情趣也取决于三观,两个三观一致的人,可能会有一些在外人看来很枯燥的生活,但TA们两人之间却觉得某件事情很有情趣。

    综上所述,对于情感质量要求较高的人;找一个拥有无限接近的三观伴侣是陪伴你一生的前提,此即为灵魂伴侣。

    而三观也分健全与否,思维也分完善与否。当你拥有70%健全三观的人,跟另一个90%健全三观的人,一开始在一起未必会匹配;如果那个70%健全三观的人没有短时间内得以成长,两人终将会有分歧。最理想的状态是:两个人的三观都接近于完善,对世间万物的看法,对看透事物的能力都趋于完善且定型。这样的三观一致,才是大道。

    那么从理论上讲,一个人一生可以遇到不止一个与你三观无限接近的人。但:
首先:即使一个各方面思维都很早熟的人,一个有极大智慧的人,当他三观趋于完善时,也往往已过而立之年。
其次:很可能下次遇到时,是十年后,对方未娶,你未嫁。结合上述那条,这个概率本不大。

     所以,你的人生中,一旦遇到一个三观与你无限接近的人,请务必珍惜与把握。她可能来的如此突然。