前言
这是jvm分享记录的最后一篇了,大致就记录下jvm运行参数设置、指令重排序含义及对java跨平台特性的理解。
jvm运行参数设置
-Xmx1024m:设置JVM最大堆内存为1024m。
-Xms1024m:设置JVM初始堆内存为1024m。此值可以设置与-Xmx相同,以避免每次垃圾回收 完成后JVM重新分配内存。
-Xss128k:设置每个线程的栈大小。jdk1.5以后每个线程栈大小为1M,之前每个线程栈大 小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成, 经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。(疑问:不知道这个参数在实际项目中是否有很大应用,至少我参与过的项目好像都没有设置该参数...)
-Xmn512m:设置年轻代大小为512m。在整个堆内存大小确定的情况下,增大年轻代将会减小年 老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
ps:上面四个为平常项目开发中常用的jvm运行参数,下面的有兴趣的读者可以作下了解:
-XX:NewSize=1024m:设置年轻代初始值为1024M。
-XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
-XX:PermSize=256m:设置持久代初始值为256M。
-XX:MaxPermSize=256m:设置持久代最大值为256M。
-XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年 轻代比年老代为1:4。
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区 (JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个 Survivor区占整个年轻代大小的1/6。
-XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还 没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接 进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一 个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活 时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度 上提高服务稳定性。
1、部分疑问解答
-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3组参数都可以影响年轻代的大小, 混合使用的情况下,优先级是什么?
如下:
高优先级:-XX:NewSize/-XX:MaxNewSize
中优先级:-Xmn(默认等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
低优先级:-XX:NewRatio
推荐使用-Xmn参数,原因是这个参数简洁,相当于一次设定 NewSize/MaxNewSIze,而且两 者相等,适用于生产环境。-Xmn 配合 -Xms/-Xmx,即可将堆内存布局完成。
-Xmn参数是在JDK 1.4 开始支持。
java跨平台特性
在学习了解完java的类文件结构之后,对java的跨平台特性突然有了深刻的理解。这里我们首先得明确我们所说的语言跨平台是指编译后的文件跨平台而不是源程序跨平台,否则那不是所有语言都具有跨平台特性啦?首先举个小例子:
我们知道,只要是用标准C开发的程序,使用不同的编译器编译后的可执行文件是可以在对应平台运行的,比如windows可以使用VC编译,那编译后的exe文件就可以在windows下运行;liunx下可以使用GCC编译,生成的可执行文件就可以在Liunx上运行。那VC编译的exe能在Liunx上运行吗?答案当然是否定的,使用特定编译器编译的程序只能在对应的平台运行,这里也可以说编译器是与平台相关的,编译后的文件也是与平台相关的。
1、语言跨平台原理
要想实现语言跨平台,那么就不能编译成机器语言,那样则与平台相关了,需要编译为中间语言,再由解释器解释执行。
看了上面的原理介绍,然后想想我们java,有没有一丝熟悉?java的跨平台性正是基于上述原理,采用“先编译后解释”,中间语言即为.class字节码文件,既然有了中间语言,然后就需要jvm出马了,java虚拟机负责将字节码翻译成特定平台下的机器码然后运行。也就是说,只要在不同平台上安装对应的jvm,那么就可运行字节码文件,进而运行java程序,最终可实现"一次编译,到处运行的"目标。
jvm是一个桥梁,是一个中间件,不同平台编译出来的字节码是相同的,但是不同的jvm编译出来的机器码是不同的,这与jvm的版本有关。这里要注意跨平台的是java程序,不是jvm。jvm是用C/C++开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的jvm。
2、java的语言无关性
下面给出java虚拟机提供的针对语言无关性的一张图(方便大家更好的理解跨平台性):
解释下:java虚拟机不和包括java在内的任何语言绑定,它只与.class文件这种特点的二进制文件格式所关联,.class中包含了java虚拟机指令集和符号表以及若干其他辅助信息,因此任何一门功能性语言都可以表示为一个能够被java虚拟机所接受的有效的class文件。
指令重排序
如果有看过之前的一篇文章:java内存模型小结,相信对里面提到的指令重排序可能会有点不理解,这是java为了提高性能而采取的一种机制,那何为指令重排序呢?
指令重排序是指cpu采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。但并不是说指令任意重排,cpu需要能正确处理指令依赖情况以保障程序能得出正确的执行结果。譬如指令1把地址A中的值加10,指令2把地址A中的值乘以2,指令3把地址B中的值减去3,这时指令1和指令2是有依赖的,它们之间的顺序不能重排 --- (A+10) * 2与A*2+10显然是不相等的,但指令3可以重排到指令1、2之前或者中间,只要保证cpu执行后面依赖到A\B值得操作时能获取到正确的A和B值即可。
开发中无依赖的例子(数据库):
然而在实际开发中,偶尔还是发生奇怪的现象,即明明写在前面的代码(例如往表中插入某条数据)却后执行,而后执行的某段代码中恰巧存在需要查询刚插入的数据,此时由于这两段代码无依赖,因此发生指令重排序导致查询一直为空,最终得到的结果与想象中的一直不同。