本部分,从攻城狮编写.java文件入手,详解了编译、载入、AOP原理。
读过《程序员的自我修养》的朋友,对程序的编译及执行会有一个很清晰的认识:编译其实就是将人类能理解的代码文件转译为机器/CPU能执行的文件(包括数据段、代码段),而执行的过程,则是根据文件头部字节的标识(简称魔数),映射为对应的文件结构体,找到程序入口,当获取到CPU执行权限时,将方法压栈,执行对应的指令码,完成相应的逻辑操做。
而对应.java文件,则先须要使用javac进行编译,编译后的.class文件,此文件将java程序能读懂的数据段和代码段,以后用java执行文件,既是载入.class文件,找到程序入口,并根据要执行的方法,不停的压栈、出栈,进行逻辑处理。html
在加载阶段,虚拟机须要完成如下三件事情:java
- 经过一个类的全限定名来获取其定义的二进制字节流。
- 将这个字节流所表明的静态存储结构转化为方法区的运行时数据结构。
- 在Java堆中生成一个表明这个类的java.lang.Class对象,做为对方法区中这些数据的访问入口。
即至关于在内存中将代码段和数据段关联起来,组织好Class对象的内存空间,做为对象成员和方法的引入入口,并将.class及方法载入Perm内存区。相对于类加载的其余阶段而言,加载阶段(准确地说,是加载阶段获取类的二进制字节流的动做)是可控性最强的阶段,由于开发人员既可使用系统提供的类加载器来完成加载,也能够自定义本身的类加载器来完成加载。程序员
分为四个阶段:算法
这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。验证阶段大体会完成4个阶段的检验动做:
文件格式验证验证字节流是否符合Class文件格式的规范;例如:是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围以内、常量池中的常量是否有不被支持的类型。
元数据验证对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object以外。
字节码验证经过数据流和控制流分析,肯定程序语义是合法的、符合逻辑的。
符号引用验证确保解析动做能正确执行。spring
验证阶段是很是重要的,但不是必须的,它对程序运行期没有影响,若是所引用的类通过反复验证,那么能够考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。数据库
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有如下几点须要注意:
一、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。编程
二、这里所设置的初始值一般状况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。如public static int value = 3;那么变量value在准备阶段事后的初始值为0,而不是3,由于这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器数组
三、若是类字段的字段属性表中存在ConstantValue属性,即同时被final和static修饰,那么在准备阶段变量value就会被初始化为ConstValue属性所指定的值。如public static final int value = 3;编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为3。浏览器
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动做主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,能够是任何字面量。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。缓存
这个阶段的主要目的将编译后的虚拟地址(相似动态库,库数据段都是0x00开始,载入内存后须要与实际分配的地址关联起来)与实际运行的地址关联起来。
一、假如这个类尚未被加载和链接,则程序先加载并链接该类
二、假如该类的直接父类尚未被初始化,则先初始化其直接父类
三、假如类中有初始化语句,则系统依次执行这些初始化语句
类初始化时机:只有当对类的主动使用的时候才会致使类的初始化,类的主动使用包括如下六种:
– 建立类的实例,也就是new的方式
– 访问某个类或接口的静态变量,或者对该静态变量赋值
– 调用类的静态方法
– 反射(如Class.forName(“com.xxx.Test”))
– 初始化某个类的子类,则其父类也会被初始化
– Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类。
JVM最底层的载入是BootstrapClassLoader,为C语言编写,从Java中引用不到,其上是ExtClassLoader,而后是AppClassLoader,普通类中getContextClassLoader()获得的是AppClassLoader,getContextClassLoader().getParent()获得的是AppClassLoader,而再往上getParent()为null,即引用不到BootstrapLoader。
启动类加载器:BootstrapClassLoader,负责加载存放在JDK\jre\lib(JDK表明JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,而且能被虚拟机识别的类库(如rt.jar,全部的java.*开头的类均被BootstrapClassLoader加载)。启动类加载器是没法被Java程序直接引用的。
扩展类加载器:ExtensionClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载DK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的全部类库(如javax.*开头的类),开发者能够直接使用扩展类加载器。
应用程序类加载器:ApplicationClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者能够直接使用该类加载器,若是应用程序中没有自定义过本身的类加载器,通常状况下这个就是程序中默认的类加载器。
应用程序都是由这三种类加载器互相配合进行加载的,若是有必要,咱们还能够加入自定义的类加载器。由于JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,所以若是编写了本身的ClassLoader,即可以作到以下几点:
1)在执行非置信代码以前,自动验证数字签名。
2)动态地建立符合用户特定须要的定制化构建类。
3)从特定的场所取得java class,例如数据库中和网络中。
双亲委派机制
简单来讲既是先拿父类加载器加载class,父类加载失败后再使用本类加载器加载。当AppClassLoader加载一个class时,它首先不会本身去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。当ExtClassLoader加载一个class时,它首先也不会本身去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。若是BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,若是AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
其它相关点
1.全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其余Class也将由该类加载器负责载入,除非显示使用另一个类加载器来载入。
2.父类委托,先让父类加载器试图加载该类,只有在父类加载器没法加载该类时,才使用本类加载器从本身的类路径中加载该类。
3.缓存机制,缓存机制将会保证全部加载过的Class都会被缓存,当程序中须要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为何修改了Class后,必须重启JVM,程序的修改才会生效。
--
AOP面向切面编程
AOP 专门用于处理系统中分布于各个模块(不一样方法)中的交叉关注点的问题,在 Java EE 应用中,经常经过AOP来处理一些具备横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,在不改变已有代码的状况下,静态/动态的插入代码。
将AOP放到这里的主要缘由是由于AOP改变的class文件,达到嵌入方法的目的,静态模式使用AspectJ进行由.java到.class文件编译。而动态模式时使用CGLIB载入使用javac编译的.class文件后,使用动态代理的方式,将要执行的方法嵌入到原有class方法中,完成在内存中对class对象的构造,这也就是所谓动态代理技术的内在原理。同时静态方式在载入前已经修好完.class文件,而动态方式在.class载入时须要作额外的处理,致使性能受到必定影响,但其优点是无须使用额外的编译器。整体的技术的切入点在于在修改机器执行码,达到增长执行方法的目的。参考我。
数据分类:基本类型与引用类型
基本类型包括:byte,short,int,long,char,float,double,boolean,returnAddress
引用类型包括:类类型,接口类型和数组。
数据存储:使用堆存储对象信息
方法调用:使用栈来解决方法嵌套调用,而栈内部由一个个栈帧构成,调用一个方法时,在当前栈上压入一个栈帧,此栈帧包含局部变量表,操做栈等子项,每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。表面上代码在运行时,是经过程序计数器不断执行下一条指令;而实际指令运算等操做是经过控制操做栈的操做数入栈和出栈,将操做数在局部变量表和操做栈之间转移。参见我。
这里对内存的分配作个深刻的扩展,解释下基础类型的自动装箱 boxing
以"Object obj = new Object();"为例,一个空Object对象占用8byte空间,而obj引用占用4byte,此条语句执行后共占用12byte;而Java中对象大小是8的整数倍,则Boolean b = new Boollean(true)至少须要20byte(16byte+4byte),而若是直接使用基本数据类型boolean b = true则仅仅须要1byte,在栈帧中存储;为优化此问题,JVM提出了基本类型的自动装载技术,来自动化进行基本类型与基本类型对象间的转换,来下降内存的使用量。
内存结构主要有三大块:堆内存、方法区和栈。
1.堆内存是JVM中最大的一块由Young Generation(年轻代、新生代)和Old Generation(年老代)组成,而Young Generation内存又被分红三部分,Eden空间、From Survivor空间、To Survivor空间,默认状况下年轻代按照8:1:1的比例来分配。
2.方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆)。
3.栈又分为java虚拟机栈(方法执行的内存区,每一个方法执行时会在虚拟机栈中建立栈帧)和本地方法栈(虚拟机的Native方法执行的内存区)主要用于方法的执行。
4.内存设置参数:
-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代最小空间大小。
-XX:MaxPermSize设置永久代最大空间大小。
-Xss设置每一个线程的堆栈大小。
-XX:SurvivorRatio=x #Eden区与Survivor区的大小比值,默认为8
-XX:NewRatio=x #年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)默认为2
对象分配先从Eden空间与From Survivor空间获取内存,当二者中空间不足时,进行Minor GC,将Eden与From Survivor空间存活的对象,Copy到To Survivor空间,而后清空Eden与From Survivor空间,以后将To Survivor空间对象年龄加1,并将To Survivor空间设置为From Survivor空间,保证Minor GC时To Survivor空间始终为空;而当对象年龄为15后(默认是 15,能够经过参数-XX:MaxTenuringThreshold 来设定),将存活对象放入老年代。
例外状况:
1.对于一些较大的对象(即须要分配一块较大的连续内存空间)则是直接进入到老年代。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。避免在新生代采用复制算法收集内存时,在Eden区及两个Survivor区之间发生大量的内存复制。
2.为了更好的适应不一样的程序,虚拟机并非永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,若是在Survivor空间中相同年龄全部对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象能够直接进入Old Generation,无需等到MaxTenuringThreshold中要求的年龄。
这里的疑问,按照默认的8:1:1设置,一个To Survivor空间占10%空间,每次Minor GC能保证To Survivor空间够用吗?IBM的研究代表,98%的对象都是很快消亡的,大部分的对象在建立后很快就再也不使用。这里能够根据GC detail来查看和分析比例设置是否合理。
工做:同时回收年轻代、年老代,按照配置的不一样算法进行回收。
时机:在Minor GC触发时,会检测以前每次晋升到老年代的平均大小是否大于老年代的剩余空间,若是大于,改成直接进行一次Full GC;若是小于则查看HandlePromotionFailure设置(是否容许担保,使用Old Gerneration空间担保),若是容许,那仍然进行Minor GC,若是不容许,则也要改成进行一次Full GC。
取平均值进行比较其实仍然是一种动态几率的手段,也就是说若是某次Minor GC存活后的对象突增,大大高于平均值的话,依然会致使担保失败,这样就只好在失败后从新进行一次Full GC。
首先判断对象是否存活,通常有两种方式:
- 引用计数:每一个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时能够回收。此方法简单,没法解决对象相互循环引用的问题。
- 可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证实此对象是不可用的、不可达对象。
在Java语言中,GC Roots包括:
--虚拟机栈中引用的对象。
--方法区中类静态属性实体引用的对象。
--方法区中常量引用的对象。
--本地方法栈中JNI引用的对象。
标记-清除算法:首先标记出全部须要回收的对象,在标记完成后统一回收掉全部被标记的对象。之因此说它是最基础的收集算法,是由于后续的收集算法都是基于这种思路并对其缺点进行改进而获得的。
缺点:一个是效率问题,标记和清除过程的效率都不高;另一个是空间问题,标记清除以后会产生大量不连续的内存碎片,空间碎片太多可能会致使,当程序在之后的运行过程当中须要分配较大对象时没法找到足够的连续内存而不得不提早触发另外一次垃圾收集动做。
复制算法:对空间问题的改进,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,而后再把已使用过的内存空间一次清理掉。
缺点是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则致使效率下降。若是不想浪费50%的空间,就须要有额外的空间进行分配担保(HandlePromotionFailure设置为true),以应对被使用的内存中全部对象都100%存活的极端状况,因此在老年代通常不能直接选用这种算法。
标记-压缩/整理算法:对复制算法在老年代上的改进,标记过程仍然与“标记-清除”算法同样,但后续步骤不是直接对可回收对象进行清理,而是让全部存活的对象都向一端移动,而后直接清理掉端边界之外的内存。
分代收集算法:把Java堆分为新生代和老年代,这样就能够根据各个年代的特色采用最适当的收集算法。
在新生代中,每次垃圾收集时都发现有大批对象死去,只有少许存活,那就选用复制算法,只须要付出少许存活对象的复制成本就能够完成收集。
而老年代中,由于对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。
串行收集器是最古老,最稳定以及效率高的收集器,使用中止复制方法,只使用一个线程去串行回收;垃圾收集的过程当中会Stop The World(服务暂停);
参数控制:使用-XX:+UseSerialGC可使用Serial+Serial Old模式运行进行内存回收(这也是虚拟机在Client模式下运行的默认值)
缺点是串行效率较低
ParNew收集器其实就是Serial收集器的多线程版本,使用中止复制方法。新生代并行,其它工做线程暂停。
参数控制:使用-XX:+UseParNewGC开关来控制使用ParNew+Serial Old收集器组合收集内存;使用-XX:ParallelGCThreads来设置执行内存回收的线程数。
Parallel Scavenge收集器相似ParNew收集器,Parallel收集器更关注CPU吞吐量,即运行用户代码的时间/总时间,使用中止复制算法。能够经过参数来打开自适应调节策略,虚拟机会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也能够经过参数控制GC的时间不大于多少毫秒或者比例。
参数控制:使用-XX:+UseParallelGC开关控制使用Parallel Scavenge+Serial Old收集器组合回收垃圾(这也是在Server模式下的默认值);使用-XX:GCTimeRatio来设置用户执行时间占总时间的比例,默认99,即1%的时间用来进行垃圾回收。使用-XX:MaxGCPauseMillis设置GC的最大停顿时间(这个参数只对Parallel Scavenge有效),用开关参数-XX:+UseAdaptiveSizePolicy能够进行动态控制,如自动调整Eden/Survivor比例,老年代对象年龄,新生代大小等,这个参数在ParNew下没有。
Serial Old收集器:老年代收集器,单线程收集器,串行,使用"标记-整理"算法(整理的方法是Sweep(清理)和Compact(压缩),
Parallel Old 收集器
多线程机制与Parallel Scavenge差不错,使用标记整理(与Serial Old不一样,这里的整理是Summary(汇总)和Compact(压缩),汇总的意思就是将幸存的对象复制到预先准备好的区域,而不是像Sweep(清理)那样清理废弃的对象)算法,在Parallel Old执行时,仍然须要暂停其它线程。Parallel Old在多核计算中颇有用。这个收集器是在JDK 1.6中,与Parallel Scavenge配合有很好的效果。
参数控制: 使用-XX:+UseParallelOldGC开关控制使用Parallel Scavenge +Parallel Old组合收集器进行收集。
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤为重视服务的响应速度,但愿系统停顿时间最短,以给用户带来较好的体验。
从名字(包含“Mark Sweep”)上就能够看出CMS收集器是基于“标记-清除”算法实现的,它的运做过程相对于前面几种收集器来讲要更复杂一些,整个过程分为6个步骤,其中初始标记、从新标记这两个步骤仍然须要“Stop The World”,包括:
优势:并发收集、低停顿
缺点:产生大量空间碎片、并发阶段会下降吞吐量
- 堆碎片:CMS收集器并无任何碎片整理的机制,可能出现总的堆大小远没有耗尽,但由于没有足够连续的空间却不能分配对象,只能触发Full GC来解决,形成应用停顿。
参数控制
-XX:+UseConcMarkSweepGC 使用CMS收集器
当使用-XX:+UseConcMarkSweepGC时,-XX:UseParNewGC会自动开启。所以,若是年轻代的并行GC不想开启,能够经过设置-XX:-UseParNewGC来关掉
-XX:+CMSClassUnloadingEnabled相对于并行收集器,CMS收集器默认不会对永久代进行垃圾回收。
-XX:+CMSConcurrentMTEnabled当该标志被启用时,并发的CMS阶段将以多线程执行(所以,多个GC线程会与全部的应用程序线程并行工做)。该标志已经默认开启,若是顺序执行更好,这取决于所使用的硬件,多线程执行能够经过-XX:-CMSConcurremntMTEnabled禁用(注意是-号)。
-XX:+UseCMSCompactAtFullCollection Full GC后,进行一次碎片整理;整理过程是独占的,会引发停顿时间变长
-XX:+CMSFullGCsBeforeCompaction 设置进行几回Full GC后,进行一次碎片整理
-XX:ParallelCMSThreads 设定CMS的线程数量(通常状况约等于可用CPU数量)
-XX:CMSMaxAbortablePrecleanTime:当abortable-preclean阶段执行达到这个时间时才会结束
-XX:CMSInitiatingOccupancyFraction,-XX:+UseCMSInitiatingOccupancyOnly来决定什么时间开始垃圾收集;若是设置了-XX:+UseCMSInitiatingOccupancyOnly,那么只有当old代占用确实达到了-XX:CMSInitiatingOccupancyFraction参数所设定的比例时才会触发cms gc;若是没有设置-XX:+UseCMSInitiatingOccupancyOnly,那么系统会根据统计数据自行决定何时触发cms gc;所以有时会遇到设置了80%比例才cms gc,可是50%时就已经触发了,就是由于这个参数没有设置的缘由.
G1 GC是Jdk7的新特性之1、Jdk7+版本均可以自主配置G1做为JVM GC选项;做为JVM GC算法的一次重大升级、DK7u后G1已相对稳定、且将来计划替代CMS。
不一样于其余的分代回收算法、G1将堆空间划分红了互相独立的区块。每块区域既有可能属于O区、也有多是Y区,且每类区域空间能够是不连续的(对比CMS的O区和Y区都必须是连续的)。区别以下:
- G1在压缩空间方面有优点
就目前而言、CMS仍是默认首选的GC策略、可能在如下场景下G1更适合:
- 大量内存的系统提供一个保证GC低延迟的解决方案,也就是说堆内存在6GB及以上,稳定和可预测的暂停时间小于0.5秒。
G1堆由多个区(region)组成,每一个区大小1M~32M,逻辑上区有3种类型,包括(Eden、Survivor、Old),按分代划分包括:年轻代(Young Generation)和老年代(Old Generation)。
若是从 ParallelOldGC 或者 CMS收集器迁移到 G1,可能会看到JVM进程占用更多的内存(a larger JVM process size)。 这在很大程度上与 “accounting” 数据结构有关,如 Remembered Sets 和 Collection Sets。
Remembered Sets 简称 RSets。跟踪指向某个heap区内的对象引用。 堆内存中的每一个区都有一个 RSet。 RSet 使heap区能并行独立地进行垃圾集合。 RSets的整体影响小于5%。
Collection Sets 简称 CSets。收集集合, 在一次GC中将执行垃圾回收的heap区。GC时在CSet中的全部存活数据(live data)都会被转移(复制/移动)。集合中的heap区能够是 Eden, survivor, 和/或 old generation。CSets所占用的JVM内存小于1%。
Young GC是stop-the-world活动,会致使整个应用线程的中止。其过程以下:
- 初始化标记(stop_the_world事件):这是一个stop_the_world的过程,是随着年轻代GC作的,标记survivor区域(根区域),这些区域可能含有对老年代对象的引用。
-XX:+UseG1GC使用G1 GC
-XX:MaxGCPauseMillis=n设置一个暂停时间指望目标,这是一个软目标,JVM会近可能的保证这个目标
-XX:InitiatingHeapOccupancyPercent=n内存占用达到整个堆百分之多少的时候开启一个GC周期,G1 GC会根据整个栈的占用,而不是某个代的占用状况去触发一个并发GC周期,0表示一直在GC,默认值是45
-XX:NewRatio=n年轻代和老年代大小的比例,默认是2
-XX:SurvivorRatio=n eden和survivor区域空间大小的比例,默认是8
-XX:MaxTenuringThreshold=n晋升的阈值,默认是15(译者注:一个存活对象经历多少次GC周期以后晋升到老年代)
-XX:ParallelGCThreads=n GC在并行处理阶段试验多少个线程,默认值和平台有关。(译者注:和程序一块儿跑的时候,使用多少个线程)
-XX:ConcGCThreads=n并发收集的时候使用多少个线程,默认值和平台有关。(译者注:stop-the-world的时候,并发处理的时候使用多少个线程)
-XX:G1ReservePercent=n预留多少内存,防止晋升失败的状况,默认值是10
-XX:G1HeapRegionSize=n G1 GC的堆内存会分割成均匀大小的区域,这个值设置每一个划分区域的大小,这个值的默认值是根据堆的大小决定的。最小值是1Mb,最大值是32Mb
转载请注明出处,百度搜“成金之路”。
./jstat -gc pid 3s对pid GC每隔3s进行监控
另外,jstack -l long listings,会打印出额外的锁信息,在发生死锁时能够用jstack -l pid来观察锁持有状况。
--
内存泄漏OOM,一般作法:
方法1. 首先配置JVM启动参数,让JVM在遇到OutOfMemoryError时自动生成Dump文件-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path
方法2. 使用上面第3步进行进行分析;
方法3. 使用eclipse的MAT分析工具对dump文件进行分析
- 发现问题
通常结合JMX分析,分析远程tomcat状态
修改远程tomcat的catalina.sh配置文件,在其中增长(不走权限校验。只是打开jmx端口):
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=192.168.122.128 -Dcom.sun.management.jmxremote.port=18999 -Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
--
若是链接的是公网上的Tomcat,那么就要注意安全性了,接下来看看使用用户名和密码链接
JAVA_OPTS='-Xms128m -Xmx256m -XX:MaxPermSize=128m -Djava.rmi.server.hostname=10.10.23.10
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=/path/to/passwd/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/path/to/passwd/jmxremote.access'
如下分别编辑jmxremote.password与jmxremote.access两个文件
jmxremote.password
monitorRole 123456
controlRole 123456789
jmxremote.access
monitorRole readonly
controlRole readwrite
完成这两个文件后修改jmxremote.password的权限chmod 600 jmxremote.password
本质上是减小Full GC的次数(Minor GC很快基本不会有影响)
若是是频繁建立对象的应用,能够适当增长新生代大小。常量较多能够增长持久代大小。对于单例较多的对象能够增长老生代大小。好比spring应用中。
GC选择,在JDK5.0之后,JVM会根据当前系统配置进行判断。通常执行-Server命令即可以。gc包括三种策略:串行,并行,并发(使用CMS收集器老年代)。
吞吐量大大应用,通常采用并行收集,开启多个线程,加快gc的速率。
响应速度高的应用,通常采用并发收集,好比应用服务器。
年老代建议配置为并发收集器,因为并发收集器不会压缩和整理磁盘碎片,所以建议配置:
-XX:+UseConcMarkSweepGC #并发收集年老代
-XX:CMSInitiatingOccupancyFraction=80 # 表示年老代空间到80%时就开始执行CMS
-XX:+UseCMSInitiatingOccupancyOnly #是阀值生效
-XX:+UseCMSCompactAtFullCollection#打开对年老代的压缩。可能会影响性能,可是能够消除内存碎片。
-XX:CMSFullGCsBeforeCompaction=10 # 因为并发收集器不对内存空间进行压缩、整理,因此运行一段时间之后会产生“碎片”,使得运行效率下降。此参数设置运行次FullGC之后对内存空间进行压缩、整理。
若是以为不错,还请不吝推荐,您的承认是我分享的动力,同时也但愿帮助更多的人走上本身“成金之路”。谢谢!转载请注明出处,百度搜“成金之路”。