JVM知识在离线数据中的运用

简介:  首先说我们线上JVM用的Hotspot,因为是64位机器,只支持server模式的。使用默认的JIT编译器模式。Java7。因为在java8中移除了永久代,牺牲了一点性能来获取更高的安全保障。但这个程序是个后台服务,升级java8反而不合适了。

 又是飞花的季节了。多愁善感的林妹妹看到柳絮说:“嫁与东风春不管,凭尔去,忍淹留。”宝姐姐看了却来一句:“好风凭借力送我上青云”。


  特别羡慕情商高的人,经常在想他们是怎么做到的。从来看不出他们不喜欢谁,满眼里都是真诚。渐渐的开始能够理解所有的人。比如:有些女孩爱化很浓的妆,后来才想到不是所有的人都天生皮肤细,清水洗把脸就可以出门。现实的女孩大都是梦碎之后被碎片扎的遍体鳞伤,更应该被呵护才是。爱钱是因为不是所有的人都有不依靠别人就能活的能力,谁最先想到的不是衣食住行呢。但我还是理解不了浪费粮食的,不管是植物还是动物,为了我付出了自己的生命,我好歹也应该让它成为我身体的一部分,和它一起好好的活着~~


  我是一个心想事成的人,人生少走了很多弯路,少了很多思考,也少了很多对他人的理解。还好大脑的自动补全能力比较强。在超市里,我走到酒的那一排都会绕着走,因为总是感觉酒瓶子会掉下来碎掉。有次看《神盾局特工》看完了换一下节目才发现静音了。因为之前看过好几集了,看到字幕大脑里自动把声音补上了,竟然没发现。不知道这个能力和语言天赋之间有没有什么联系。但是我的大脑会存储很多的声音,很少存储图像。很少真正很关心一个人的长相。对于我,一个有磁性声音的人会更有魅力。不知道这个和写不好字的缺陷之间有没有什么联系[衰]。小鲜肉的这种能力在他一岁的时候就体现出来了。一个妈妈推着一个宝宝在前面走,他看到小推车上的水壶有一半在外面往下沉。他就大喊:要掉出来了,要掉出来了。别人没怎么样,他急得不行了。


  心想事成是一种性格,而不是能力。小鲜肉如果有想做的事儿,想要的东西,他很少直接说。他会非要别人去猜他怎么想的。我却总是想办法让他自己说出来,因为这样的一种做事方式,他会终生受益的。比如说:俞伯牙如果是我这样的性格,来了一个人,不管是谁,他在弹琴,他就会说:我弹的高山,我弹的是流水。也就不会“知音难觅”一说了。这样的人因为少了让别人去理解自己这一步,所以更多会考虑怎么理解别人。这样的人还很单纯,单纯的人脸上会独有一种“笑起来很甜”。


  《小王子》里的小主人公整个人生都在探索自己为什么会爱上那朵玫瑰。那花儿长的美,却矫情,虚荣,带给了他无尽的痛苦。最后受到了小狐狸的启发才发现:那花儿的独一无二在于他对花儿的付出,花儿对自己的驯服。其实哪有那么纠结?爱了就爱了呗。


  在现实中,会是这样一种情况。两个男孩喜欢同一个女孩子。其中一个男孩总是在想:“我拥有什么资本能让她对我不离不弃呢,我有没有比另一个男孩更优秀,如果她看清楚了真实的我会不会不喜欢我?我的付出会不会一无所获?她是不是一个好女孩?”另一个男孩只在想一件事,那就是我喜欢那个女孩子我要让她知道。一个够聪明的女孩子就算心里喜欢的前一个,无论理智还是情感,她也不会选他(碰上我这种爱给自己找麻烦的人另当别论[汗])。因为这样人的性格产生的连锁反应是:总是在权衡利弊,人会越来越自私,自我为中心,不愿意付出。想的太多,做的太少,害怕承担后果,这样的人根本没有真心去爱一个人的基本能力。而后一个男孩:想到就去做了,他会想各种方法去达到目的,做了很多,承担了很多,会更有担当和责任心。为啥我会想这些呢?好歹咱也是中科院心理学的研究生,不能白学啊!


  一定要相信周围的人都是很聪明的,自己是什么样的人,别人都是可以看出来的,所以也没有什么必要藏着掖着的。想了很多,也总有自己没有考虑到的因素。谷歌的对弈软件也不能百分百分取胜。所以,只要去做自己想做的事儿就可以了,想多想少,结果可能没有那么大的差别,失之东隅收之桑榆。做和不做,区别可就大了。


  小狐狸对小王子说:“如果你驯服了我,我的生命就会充满阳光……我不吃面包,麦子对我本没有什么意义,麦田更不会让我产生联想,可一旦你驯服了我,一切都改变了,因为你有金色的头发,再看到麦田我就会想起你,而且我还会喜欢倾听风吹满麦浪的声音……” 小狐狸忍受着最终会哭一场的后果让小王子驯服了自己。最终小王子选择了回去找自己的玫瑰。但是小狐狸的生命从此鲜活起来,一切都有了意义。


  额~~,说多了。在想离线数据这个项目的独特之处在哪里。它用到了很多和JVM打交道的地方,顺便将这个总结一下。


1112728-20170412182828298-986505016.png


 我既然把内存参数配置设置成了这个样子。我当然要知道这么庞大的资源都干了啥。特别是半夜跑全量的时候,我有次测试了一晚上,第二天早上睡觉中午醒来发现那台机器登录不了了。找运维,他们也登录不了,最后只好重启了服务器。我分析发现新生代设置的不合适,导致它没有空间进行full gc。结果内存全满了。最喜欢做这样的项目了,用现成的框架,程序会死,把机器弄死很不容易的[偷笑]


  首先说我们线上JVM用的Hotspot,因为是64位机器,只支持server模式的。使用默认的JIT编译器模式。Java7。因为在java8中移除了永久代,牺牲了一点性能来获取更高的安全保障。但这个程序是个后台服务,升级java8反而不合适了。


  处理数据对象特别大,有的压缩前30多M。因为搜索哥哥们规定实时消息一个专辑下要包含所有的视频,有的专辑下面有几万个视频[哭笑]。所以在处理这一条数据的时候,处理完的部分仍在内存中。为了可维护,晚上全量推送和其他时段的实时推送数据处理部分逻辑共用。晚上全量是用50个线程的线程池来跑的。如果50个线程内部串行,虽然我做了策略,分派处理内容的时候,将超大的专辑独立出来。一个线程处理几百个小专辑,但是大专辑只处理两三个。但是一个专辑就要十几分钟。但是专辑的分为多个区域,多种语言的,每种都要独立成一条数据,全量文件专辑我定义了共440个线程,每个线程独立压缩生成一个文件,增量时发消息也是扔到MQ里不用管。这些不需要通信的地方完全可以异步处理。异步又涉及到总是新建线程的堆回收问题。所以我将这些线程类都放到了对象池里进行管理。但是全量的时候的堆占用还是很大很大的,Full GC也很频繁。空间换时间嘛,所以JVM参数配的看起来很夸张,但真不是浪费。专辑的数据量是十万级,视频的数据量是千万级。原来的离线推送系统只发送ID给搜索那边,跑全量也要4个小时。所以之前是一周跑一次全量。我做的新系统,全量生成的专辑共15个G,视频占31个G。专辑和视频跑完全量一台用20多分钟,另一台长一些(因为另一台的数据库的机器不是同一运行商,时间开销在网路上),现在我们是一天一个全量。


  -Xss这个参数的最终设置,我当时是整晚没睡觉测试的。因为这个参数是每个线程的堆栈大小。是方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧。设置的小,跑起来会慢,设置的大,跑起来快,CPU计算速度就上去了。这里面还涉及到了虚拟机的逃逸分析,可能引起CPU跑满。说到这里我是不是还得说说虚拟机栈帧的结构啊。


栈帧(Stack Frame)结构


栈帧是用于支持虚拟机进行方法执行的数据结构,是属性运行时数据区的虚拟机站的栈元素。栈帧包括:


1>局部变量表(locals大小,编译期确定),一组变量存储空间,容量以slot为最小单位。


2>操作栈(stack大小,编译期确定),操作栈元素的数据类型必须与字节码指令序列严格匹配。


3>动态链接,指向运行时常量池中该栈帧所属方法的引用,为了动态连接使用。


  前面是静态解析


  对于运行期转化为直接引用,是动态解析


4>方法返回地址。


  正常退出:执行引擎遇到方法返回的字节码,将返回值传递给调用者


  异常退出:遇到Exception没被捕获时没有返回值


5>额外附加信息:由具体虚拟机实现。


说到这里我是不是还应该画一张java的内存模型啊。大体是这个样子的:


1112728-20170412182934720-647812933.png


在这个地方想起来一个习惯,就是我用的集成开发环境是Eclipse,喜欢用它因为它是纯java写的。而且我经常把它搞死。每次把它弄死了我都会很认真的去分析它死掉的原因。对深入了解jvm很有帮助。


1112728-20170412182951767-1458999700.png


 有必要贴一下垃圾收集日志。这是线上比较老的GC日志。因为我已经把打印gc日志这个选项关闭很久了。大家可能注意到了我的新生代设置的特别大,37g。这不仅仅是因为测试时服务器挂过。而是我说了,处理的数据对象特别大,这些对象都是朝生暮死的。如果新生代设置的不够大,这些大对象就会直接进入老年代,大大降低了垃圾回收的效率。大家也可以看出来,对新生代的垃圾回收是相当有成效的,99%都被回收了。


  这里介绍一下数据结构中的栈和堆与内存分配中的栈和堆:


  数据结构中的栈是一种后进先出性质的数据结构,像一个桶。取数据不能像数组那样想取哪个取哪个。必须先把想取的数据之后进来的数据全pop出去。


  数据结构中的堆通常指二叉堆。分为最大堆和最小堆(我还用这个数据结构实现一种加密机制获得了专利)。它的存取就要比栈灵活。


  如果是C++出身的程序员对于内存分配的栈和堆理解就完全不是问题。因为java就是c++写的。内存中的栈区处理相对较高的地址以地址,不断的分配,分配的地址增大。栈地址是相反的。所以在c++语言中和jvm中,栈都是系统自动分配空间的,速度快。而堆是需要申请的,我记得是malloc函数。栈上的数据的生存周期是在函数的运行过程中,运行后就释放掉,不可以再访问。堆上的数据只要程序员不释放空间,就一直可以访问到。这就是为什么java栈是线程隔离的,而堆是线程共享的。

相关文章
|
6月前
|
消息中间件 存储 Java
jvm性能调优实战 - 47超大数据量处理系统是如何OOM的
jvm性能调优实战 - 47超大数据量处理系统是如何OOM的
76 0
|
1月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
63 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
31 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
5月前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
65 2
|
6月前
|
存储 算法 Java
JVM 数据区域
JVM 数据区域
|
6月前
|
存储 Java 编译器
【JVM】运行时数据区域
【JVM】运行时数据区域
44 0
|
6月前
|
存储 算法 Java
JVM-01Java内存区域与内存溢出异常(上)【运行时区域数据】
JVM-01Java内存区域与内存溢出异常(上)【运行时区域数据】
55 0
|
6月前
|
存储 算法 Java
[JVM] 美团二面,说一下JVM数据区域
[JVM] 美团二面,说一下JVM数据区域
|
Arthas 消息中间件 监控
JVM第五讲:纵横数据如何应对洪峰推送
JVM第五讲:纵横数据如何应对洪峰推送
JVM第五讲:纵横数据如何应对洪峰推送
|
存储 Java 数据管理
【面试题精讲】JVM-运行时数据区-帧数据
【面试题精讲】JVM-运行时数据区-帧数据