java虚拟机调优(JVM性能调优-G1)

本文目录
JVM性能调优-G1
本篇是对Java官网G1收集器调优的精简版。针对G1垃圾的收集阶段可能出现的问题,非合理内存分配,大对象占用,Full GC等问题作出解决方式和操作参数。
G1是一个吞吐量和时间延迟之间相互平衡的收集器。目标是高吞吐量下提供相对较小、统一的暂停。
所以如果是交互性强的应用程序,使用G1时需要基于时延优先进行考虑。
虚拟机从操作系统内存中分配或归还内存可能会导致不必要的延迟。通过使用选项-Xms和-Xmx将最小和最大堆大小设置为相同的值,并使用 - XX:+AlwaysPreTouch 预触摸所有内存,以将这项工作移到VM启动阶段,从而避免延迟。
并行处理 Reference对象,ParallelRefProcEnabled默认值false,若 GC log 里出现 Reference 处理时间较长的日志,可以开启此参数- XX:+ParalleRefProcEnabled 。开启后会使用jvm可用的线程数进行处理,但官网上提到的-XX:ReferencesPerThread参数在jdk17的版本中没有找到,猜测可能是jvm内部控制不再作可调试的参数。
年轻代收集所花费的时间大致与年轻代的大小成正比。官网给出的两个参数- XX:G1NewSizePercent ,-XX: G1MaxNewSizePercent 在jdk17中没有找到,在没有固定年轻代大小时,G1会进行动态调整,所以这个调优的参考性不大,可以忽略。
减少老年代regions暂停时间
RS是一个抽象的数据结构,具体的实现由table card完成。一般会把记忆集和卡表放在一起讨论。简单来讲就是所有对象引用关系的一个集合,GCRoot时扫描的不是去实际的内存区域,否则跨代引用时从新生代到老年代会是一个漫长的过程。RS很好的解决了跨代引用的问题。由于RS会动态更新,垃圾收集必须先等RS更新完毕后才去执行。所以RS更新如果耗时过长则会影响回收时间。
RS的大小跟堆空间是成正比的。
扫描RS时间也由G1为保持低存储容量而执行的压缩量决定。记忆的集合存储在内存中越紧凑,在垃圾收集期间检索存储的值所花费的时间就越多。G1自动执行这种压缩,称为记忆集粗化,同时根据该区域记忆集的当前大小更新记忆集。特别是在最高压缩级别时,检索实际数据可能非常慢。
使用选项- XX:G1SummarizeRSetStatsPeriod 结合gc+remset=trace级别日志显示是否发生粗化。
解决方案
操作选项
Java JVM怎么学习啊从哪方面入手
Java JVM如何学习?首先要了解他的类型!
java虚拟机学习
数据类型(基本类型、引用类型)====》数值本身、引用值 2. 堆、栈=====》栈是运行时的单位(如何处理数据),堆是存储的单位(数据如何存储)
------栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。在Java中,Main函数就是栈的起始点,也是程序的起始点。堆中存的是对象,栈中存的是基本数据类型和堆中对象的引用
Java中的参数传递时传值呢?还是传引用?
a) 程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引
用的问题。不会直接传对象本身。
b) Java在方法调用传递参数时,因为没有指针,所以它都是进行传值调用
c) 堆和栈中,栈是程序运行最根本的东西。程序运行可以没有堆,但是不能没有栈。
而堆是为栈进行数据存储服务,说白了堆就是一块共享的内存。不过,正是因为堆和栈的分离的思想,才使得Java的垃圾回收成为可能
Java对象的大小
a) 一个空Object对象的大小是8byte(没有任何属性的对象) b) Object ob = new Object();所占的空间为:4byte+8byte。4byte是上面部分所说的Java
栈中保存引用的所需要的空间。而那8byte则是Java堆中对象的信息.不论什么样的Java对象,其大小都必须是大于8byte
c) Java在对对象内存分配时都是以8的整数倍来分,基本类型包装类的大小至少是
16byte
引用类型
a) 对象引用类型分为强引用、软引用、弱引用和虚引用。
b) 强引用:就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收
时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收 c) 软引用:软引用一般被作为缓存来使用。与强引用的区别是,软引用在垃圾回收时,
虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。 d) 弱引用:弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在
进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。
JVM调优总结(三)-基本垃圾回收算法 7. 垃圾回收面临的问题
a) 如何区分垃圾。垃圾回收的起点是一些根对象(java栈, 静态变量, 寄存器...)。而
最简单的Java栈就是Java程序执行的main函数 b) 如何处理碎片。“复制”方式和“标记-整理”方式,都可以解决碎片的问题。 c) 如何解决同时存在的对象创建和对象回收问题;在现有的垃圾回收方式中,要进行
垃圾回收前,一般都需要暂停整个应用(即:暂停内存的分配),然后进行垃圾回收,回收完成后再继续应用
8. 分代垃圾回收详述
a) 为什么要分代:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可
以采取不同的收集方式,以便提高回收效率。
b) 如何分代虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(OldGeneration)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的
什么情况下触发垃圾回收GC有两种类型:Scavenge GC(当新对象生成,并且在Eden
申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区)和Full GC(对整个堆进行回收)
典型配置举例
a) 堆大小设置
b) 回收器选择。串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小
数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。
垃圾回收的瓶颈
a) 传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最
小,把应用的吞吐量推到了一个极限。但是他无法解决的一个问题,就是Full GC所带来的应用暂停
学习java的伙伴加我的 群去下载视频资料 540-998-279!
怎么做JAVA程序性能优化
1)尽量指定类、方法的final修饰符。带有final修饰符的类是不可派生的,Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,此举能够使性能平均提高50%。
2)尽量重用对象。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此生成过多的对象将会给程序的性能带来很大的影响。
3)尽可能使用局部变量。调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建速度较慢。
4)慎用异常。异常对性能不利,只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。
5)乘法和除法使用移位操作。用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,但是移位操作虽然快,可能会使代码不太好理解,因此最好加上相应的注释。
6)尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用 Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销。
尽量在合适的场合使用单例。使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例。
android内存优化
Android内存优化实践
1.内存模型与分布
我们知道android应用大多是使用java语言进行开发的,这就需要我们了解java的内存模型,此外在android中的应用都是基于Dalvik 虚拟机或者ART虚拟机,那么对这些虚拟机的内存分布也应该有所了解。
上图是常见的java虚拟机的内存分布图:
方法区:主要存储虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码等数据。内存优化时这一部分主要考虑是不是加载了很多不必要的第三方库。这部分的内存减少主要是常量池的回收和类的卸载(类卸载条件:无引用,类加载器可卸载)
堆:几乎所有的对象都在这个区域产生,该区域属于线程共享的区域,所以写代码时更要注意多线程安全。这个内存区域的大小变化主要是对象的创建和回收,比如:如果短时间内有大量的对象创建和回收,可能会造成内存抖动,如果对象创建之后一直回收不掉,则会导致内存泄漏,严重的内存泄漏会导致频繁的gc,从而是界面卡顿。
虚拟机栈:这个区域描述的是java方法执行的内存模型,我们常说的方法栈的入栈就是将方法的栈帧存储到虚拟机栈,这个区域是线程私有的,其生命周期就是线程的生命周期。也就是说每个线程都会有,默认一个线程的线程栈大小是1M,这不包括在方法中产生的其他对象的大小。这一块我们能控制的就是线程的数量,特别是程序中没有使用线程池或者使用的多个第三方库都带有线程池的情况。
本地方法栈:同虚拟机栈的作用非常类似,是为虚拟机执行native方法服务的,所以需要注意的地方也和虚拟机栈一样,特别是使用了第三方so的情况
程序计数器:当前线程执行的虚拟机字节码的行号记录器,占用的内存较小,可以不考虑
2.内存限制
android是基于Linux系统的,android中的进程分为两种:
1.native进程:采用C/C++实现,不包含dalvik实例的linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的
2.java进程:实例化了dalvik虚拟机实例的linux进程,进程的入口main函数为java函数。dalvik虚拟机实例的宿主进程是fork()系统调用创建的linux进程,所以每一个android上的java进程实际上就是一个linux进程,只是进程中多了一个dalvik虚拟机实例
我们知道,操作系统对进程的内存是有限制的,而且操作系统对dalvik虚拟机自身的堆内存大小也是有限制的。可以通过如下命令查看限制大小:
adb shell getprop | grep dalvik.vm.heapgrowthlimit
可以在Androidmanifest文件中application节点加入android:largeHeap=“true”来增加其dalvik虚拟机中堆的大小
我们常说的堆大小其实是包涵两部分的,一是java的堆,而是native的堆,java堆中主要是一下java对象,由 C/C++申请的内存空间则在native堆中,也有一些对象需要结合native和java堆共同完成,比如bitmap,bitmap分为bitmap对象和其中存储的像素值,对象分配在java堆,而存储的像素值则根据版本不同存储的位置也不同,api 11 - api 25是存储在java堆中的,其他版本是存储在native堆中的;
3.内存泄漏
常见的内存泄漏:
1.静态引用(自身代码和第三方代码)
2.集合内引用
3.Handler消息未清除
4.非静态的内部类中持有外部内的应用
5.匿名内部类/非静态内部类和异步线程
检查的方式:
我这里使用的是leakcanary,一般简单的内存泄漏可以直接在leakcanary中查到引用链路,不能查看的我是使用MAT来分析的当前内存信息;
上图中各项详细的指标的意义可以在这里查到,这里主要占比比较大的几个区域:
allocated:表示app内分配的java的对象数,从当前数值可以看出程序内可能存在过多创建对象的情况,比如string对象
Native:从 C 或 C++ 代码分配的对象内存,频繁进出相关页面发现native堆的大小并没有减小,说明存在c/c++层的内存泄漏
Code:您的应用用于处理代码和资源(如 dex 字节码、已优化或已编译的 dex 码、.so 库和字体)的内存。这个区域能优化的就是移除不需要的so库,懒加载使用so库,移除无用代码(import,方法和类)
4.优化实践
了解了android中的内存分布和泄漏相关,接下来就是结合自身业务进行内存优化了,如下:
1.先解决程序中内存占用较大的业务模块中的内存泄漏,不熟悉MAT的使用的可以看看这个
2.移除程序中多余的代码和引用,这里使用默认的lint检测再配合shrinkResources来删除无效资源
3.优化图片,保证图片放置在合理的文件夹,根据View大小加载合适的图片大小,根据手机状态配置bitmap和回收策略
4.优化对象创建,比如string,使用对象池等
JVM调优常用参数配置
说明:
1、一般初始堆和最大堆设置一样,因为:现在内存不是什么稀缺的资源,但是如果不一样,从初始堆到最大堆的过程会有一定的性能开销,所以一般设置为初始堆和最大堆一样。64位系统理论上可以设置为无限大,但是一般设置为 4G ,因为如果再大,JVM进行垃圾回收出现的暂停时间会比较长,这样全GC过长,影响JVM对外提供服务,所以不能太大。一般设置为4G。
2、-XX:NewRaio和-XX:SurvivorRatio这两个参数,都是设置年轻代和年老代的大小的,设置一个即可,第一是设置年轻代的大小,第二个是设置比值,理论上设置一个既可以满足需求
打印GC回收的过程日志信息
以下配置主要针对分代收集回收算法而言
年轻代的设置很关键
JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32bit还是64bit)限制:系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G-2G;64位操作系统对内存没有限制。在Windows Server 2003系统,3.5G物理内存,JDK5.0下测试,最大设置为1478m。
典型设置:
JVM给了三种选择:串行收集器,并行收集器,并发收集器,但是串行收集器只适用于小数据量的情况,一般不考虑使用了,所以这里只针对并行收集器和并发收集器。默认情况下,JDK5.0以前是使用的串行收集器,如果想使用其他收集器需要在启动时加入相应的参数, JDK5.0以后,JVM会根据系统当前的配置进行判断
吞吐量优先的并行收集器
并行收集器主要以到达一定的吞吐量为目标,适用于后台处理
响应时间优先的并发收集器
并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
6.1年轻代大小选择
响应时间优先的应用:尽可能设置大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的成都,因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8核CPU以上应用。
6.2年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可能会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考一下数据获得:
1、并发垃圾收集信息
2、持久代并发收集次数
3、传统GC信息
4、花在年轻代和年老代回收上的时间比例减少年轻代和年老代花费的时间,一般会提高应用的效率
6.3吞吐量优先的应用
一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期对象,而年老代尽存放长期存活的对象
6.4较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
Jconsole,jProfile,VisualVM
Jconsole:jdk自带, 功能简单,但是可以再系统有一定负荷的情况下使用,对垃圾回收算法有很详细的跟踪。
JProfiler:商业软件,需要付费,但是功能强大
VisualVM:JDK自带,功能强大,与Jprofiler类似,推荐
观察内存释放情况、集合类检查,对象树
上面这些调优工具都提供了强大的功能,但是总的来说一般分为以下几类功能:
一般就是根据垃圾回收前后情况对比,同时根据对象引用情况( 常见的集合对象引用 )分析,基本都可以找到泄漏点。
持久代沾满处理:
1、-XX:MaxPermSize=16m
2、换JDK比如:JRocket
系统内存被沾满:
一般是因为没有足够的资源产生线程造成的,系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大的一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,出现异常。
分配给Java虚拟机的内存越多,系统剩余的资源就越少,因此,当系统内存固定时,分配给Java虚拟机的内存越多,那么,系统总共能够产生的线程也就越少,两者成反比。同事,可以通过修改-Xss来减少分配给单个线程的空间,也可以增加系统总共生产的线程数。
java程序内存问题的诊断方法:
查看jmap的命令参数,帮助查看堆信息
java有内存溢出吗如果有是什么情况
内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。
所以我们应该明确:存在内存溢出的因不一定导致内存溢出的果。。。
1。JAVA操作文本文件为什么超过3万行就内存益处啊?
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
//PrintWriter out = new PrintWriter(fileName);
//FileOutputStream out = new FileOutputStream(fileName);
while (rs.next()) {
for (int j = 1; j 《= totalColumn; j++) {
out.write(rs.getObject(j).toString());
out.write("\t");
}
out.write("\n");
out.flush();
}
}
我在代码中 写了 out.flush()用来刷新该流的缓冲; 可是当我的记录数超过3W时就报了内存益处的问题了,难道JAVA不能边读边写吗?还是out这个对象随着指向的fileName文件的边大占用内存也大了吗??到底怎么来实现用JAVA写更多的数据而不内存益处呢
答案是:就在while(rs.next()) 当rs.next()时内存不断增大,而不是写流的问题,JAVA的ResultSet真是麻烦,而且ResultSet还不能clone(); 所以记得在做项目的时候,经常要设置:jdbc.setMaxRows(100*10000); //设置能容纳100万行记录-----这个就是防止内存泄露的哈------------------- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
jdbc.setQueryTimeout(60*30); //设置超时时间是30分钟
2。java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
public class Stack { //长生命周期
private Object; //当数组容器中没有东西时是无用的,但是无法回收~~elements是短生命周期
private int size = 0;
public void push(Object e){
ensureCapacity();
elements = e;
}
public Object pop(){
if( size == 0)
throw new EmptyStackException();
//这里还是引用了,只是指针变位置变化而已,他确实返回了那个对象,但是他却没有断开那个对象的引用,也就是说有两个地方会持有这个对象的引用,调用pop的地方,和elements中
return elements;
}
private void ensureCapacity(){
if(elements.length == size){
Object oldElements = elements;
elements = new Object;
System.arraycopy(oldElements,0, elements, 0, size);
}
}
}
上面的原理应该很简单,假如堆栈加了10个元素,然后全部弹出来,虽然堆栈是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存泄露的两个条件(必要条件):无用,无法回收。
例子1
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //这里有一个对象发生内存泄露
s.push(new Object()); //上面的对象可以被回收了,等于是自愈了,因为引用被覆盖了
}
}
因为是static,就一直存在到程序退出,但是我们也可以看到它有自愈功能,就是说如果你的Stack最多有100个对象,那么最多也就只有100个对象无法被回收其实这个应该很容易理解,Stack内部持有100个引用,最坏的情况就是他们都是无用的,因为我们一旦放新的进取,以前的引用自然消失!
内存泄露的另外一种情况:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。
这是属于: 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3。代码中存在死循环或循环产生过多重复的对象实体;
4。启动参数内存值设定的过小;
tomcat有哪些性能调优方法
Tomcat性能调优方案
一、操作系统调优
对于操作系统优化来说,是尽可能的增大可使用的内存容量、提高CPU的频率,保证文件系统的读写速率等。经过压力测试验证,在并发连接很多的情况下,CPU的处理能力越强,系统运行速度越快。。
【适用场景】 任何项目。
二、Java虚拟机调优
应该选择SUN的JVM,在满足项目需要的前提下,尽量选用版本较高的JVM,一般来说高版本产品在速度和效率上比低版本会有改进。
JDK1.4比JDK1.3性能提高了近10%-20%,JDK1.5比JDK1.4性能提高25%-75%。
因此对性能要求较高的情况推荐使用 JDK1.6。
【适用场景】 任何项目。
三、Apache集成Tomcat
Web服务器专门处理HTTP请求,应用服务器是通过很多协议为应用提供商业逻辑。虽然Tomcat也可以作web服务器,但其处理静态html的速度比不上Apache,且其作为web服务器的功能远不如Apache,因此把Apache和Tomcat集成起来,将html和Jsp的功能部分进行明确分工,让Tomcat只处理Jsp部分,其他的由Apache,IIS等web服务器去处理,由此大大提高Tomcat的运行效率。
如果一个项目中大量使用了静态页面、大量的图片等,并有有较大的访问量,推荐使用Apache集成Tomcat的方式来提高系统的整体性能。
***隐藏网址***
相对于JK的连接方式,后两种在配置上比较简单的,灵活性方面也一点都不逊色。但就稳定性而言不像JK这样久经考验,所以建议采用JK的连接方式。
Apache+JK+Tomcat配置:
***隐藏网址***
【部署步骤】
1.安装Apache服务器
2.部署Tomcat
3.将mod_jk.so拷贝到modules目录下面
***隐藏网址***
【适用场景】 大量使用静态页面的应用系统。
四、Apache和Tomcat集群
对于并发要求很高的系统,我们需要采取负载均衡的方式来分担Tomcat服务器的压力。负载均衡实现大概有四种:第一是通过DNS,但只能简单的实现轮流分配,不能处理故障;第二是基于MS IIS,windows 2003 server本身就带了负载均衡服务;第三是硬件方式,通过交换机功能或专门的负载均衡设备来实现;第四种是软件的方式,通过一台负载均衡服务器进行,上面安装软件。使用Apache Httpd Server做负载均衡器,Tomcat集群节点使用Tomcat就可以做到上述第四种方式,这种方式比较灵活,成本相对比较低,另外一个很大的优点就是可以根据应用情况和服务器的情况做一些灵活的配置。所以推荐使用Apache+Tomcat集群来实现负载均衡。
采用Tomcat集群可以最大程度的发挥服务器的性能,可以在配置较高的服务器上部署多个Tomcat,也可以在多台服务器上分别部署Tomcat,Apache和Tomcat整合的方式还是JK方式。经过验证,系统对大用户量使用的响应方面,Apache+3Tomccat集群》 Apache+2Tomcat集群 》 Apache集成Tomcat 》 单个Tomcat。并且采用Apache+多Tomcat集群的部署方式时,如果一个Tomcat出现宕机,系统可以继续使用,所以在硬件系统性能足够优越的情况下,需要尽量发挥软件的性能,可以采用增加Tomcat集群的方式。
***隐藏网址***
Apache需要调整运行参数,这样才能构建一个适合相应网络环境的web服务。其中可进行的优化配置如下:
1.设置MPM(Multi Processing Modules多道处理模块)。ThreadPerChild,这个参数用于设置每个进程的线程数,在Windows环境下默认值是64,最大值是1920,建议设置为100-500之间,服务器性能高的话值大一些,反之小一些。MaxRequestPerChild表示每个子进程能够处理的最大请求数。这个参数的值更大程度上取决于服务器的内存,如果内存比较大的话可以设置为很大的参数,否则设置一个较小的值,建议值是3000.
2.关闭DNS和名字解析 HostnameLookups off
3.打开UseCanonicalName模块 UseCanonicalName on
4.关闭多余模块 一般来说,不需要加载的模块有,mod_include.so、mod_autoindex.so、mod_access.so、mod_auth.so.
5.打开KeepAlive支持
KeepAlive on, KeepAliveTimeout 15 MaxKeepAliveRequests 1000
根据实际经验,通过Apache和Tomcat集群的方式提高系统性能的效果十分明显,这种方式可以最大化的利用硬件资源,通过多个Tomcat的处理来分担单Tomcat时的压力。
【部署步骤】
1.安装Apache服务器
2.部署Tomcat集群,即多个相同的Tomcat。
3.将mod_jk.so拷贝到modules目录下面
***隐藏网址***
【适用场景】 并发用户量及在线使用用户数量比较高的系统。
五、Tomcat自身优化
1.JVM参数调优:-Xms《size》 表示JVM初始化堆的大小,-Xmx《size》表示JVM堆的最大值。这两个值的大小一般根据需要进行设置。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。在catalina.bat中,设置JAVA_OPTS=’-Xms256m -Xmx512m’,表示初始化内存为256MB,可以使用的最大内存为512MB。
2.禁用DNS查询
当web应用程序向要记录客户端的信息时,它也会记录客户端的IP地址或者通过域名服务器查找机器名转换为IP地址。DNS查询需要占用网络,并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的IP的过程,这样会消耗一定的时间。为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值:
Tomcat4
《Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="80" minProcessors="5" maxProcessors="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" useURIValidationHack="false" disableUploadTimeout="true" /》
Tomcat5
《Connector port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true"/》
3.调整线程数
通过应用程序的连接器(Connector)进行性能控制的的参数是创建的处理请求的线程数。Tomcat使用线程池加速响应速度来处理请求。在Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。
Tomcat4中可以通过修改minProcessors和maxProcessors的值来控制线程数。这些值在安装后就已经设定为默认值并且是足够使用的,但是随着站点的扩容而改大这些值。minProcessors服务器启动时创建的处理请求的线程数应该足够处理一个小量的负载。也就是说,如果一天内每秒仅发生5次单击事件,并且每个请求任务处理需要1秒钟,那么预先设置线程数为5就足够了。但在你的站点访问量较大时就需要设置更大的线程数,指定为参数maxProcessors的值。maxProcessors的值也是有上限的,应防止流量不可控制(或者恶意的服务攻击),从而导致超出了虚拟机使用内存的大小。如果要加大并发连接数,应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。
在Tomcat5对这些参数进行了调整,请看下面属性:
maxThreads Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。
acceptCount 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。
connnectionTimeout 网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。
minSpareThreads Tomcat初始化时创建的线程数。
maxSpareThreads 一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。
最好的方式是多设置几次并且进行测试,观察响应时间和内存使用情况。在不同的机器、操作系统或虚拟机组合的情况下可能会不同,而且并不是所有人的web站点的流量都是一样的,因此没有一刀切的方案来确定线程数的值。
六、APR库使用
Tomcat中使用APR库,其实就是在Tomcat中使用JNI的方式来读取文件以及进行网络传输。可以大大提升Tomcat对静态文件的处理性能,同时如果你使用了HTTPS方式传输的话,也可以提升SSL的处理性能。
一般在Windows下,可以直接下载编译好的二进制版本的dll库文件来使Tomcat启用APR,一般建议拷贝库文件tcnative-1.dll到Tomcat的bin目录下。而在Linux下,可以直接解压和安装bin目录下的tomcat_native.tar.gz文件,编译之前要确保apr库已经安装。
怎么才能判断Tomcat是否已经启用了APR库呢?方法是通过看Tomcat的启动日志:
如果没有启用APR,则启动日志一般有这么一条:
***隐藏网址***
如果启用了APR,则这条日志就会变成:
***隐藏网址***
***隐藏网址***
调优综述
根据以上分析,如果想要Tomcat达到最优的效果,首先要争取使得操作系统以及网络资源达到最优,并且最好使用高版本的JDK。对于有大量静态页面的系统,采用Apache集成Tomcat的方式,把静态页面交由Apache处理,动态部分交由Tomcat处理,能极大解放Tomcat的处理能力。使用ARP库也能极大的提高Tomcat对静态文件的处理能力。对于并发要求较高的系统,采用Apache加Tomcat集群的方式,将负载分别分担到多个Tomcat上,能很大的提高系统的性能,充分利用硬件资源。同时需要对Tomcat自身进行优化,包括增大内存、调节并发线程数等。

更多文章:
server2008官网下载(sql server 2008 r2 express 官方)
2026年9月7日 13:00
域名邮箱域名续费(企业邮箱提示域名已过期,域名不在我这,怎么续费)
2026年9月7日 11:00
菜鸡免费版安卓破解下载(菜鸡云游戏最新版破解版永久免费无限时间下载地址)
2026年9月7日 02:10






