Tomcat调优

Tomcat服务器内存大小中的 Xms, Xmx, PermSize, MaxPermSize

 

  Jvm按照其存储数据的内容将所需内存分配为堆区 和非堆区两个部分:
  所谓堆区即为通过 new 的方式创建的对象(类实例)所占用的内储存空间,堆是运行时数据区域。
  非堆区即为代码、常量、外部访问(例如文件访问流所占资源)等。
  配置堆区(-Xms,-Xmx,-XX:newSize,-XX:MaxNewSize,-Xmn)
  配置非堆区(-XX:PermSize,-XX:MaxPermSize)
 
堆区配置参数:
  1. -Xms:表示 Java虚拟机堆区内存初始内存分配的大小,虚拟机在启动时向系统申请的内存的大小。
  2. -Xmx:表示 Java虚拟机堆区内存可分配的最大上限,通常为操作系统可用内存的 1/4 大小。但是开发过程中,通常会将 -Xms 与 -Xmx 两个参数配置相同的值,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源。
  如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象 进行初始化,虚拟机就必须重复的增加内存来满足使用。默认 空余堆内存村小于 40% 时,JVM 就会增大堆,直到 -Xmx 的最大限制;空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制。因此服务器一般设置 -Xms,-Xmx 相等以避免每次 GC 后调整堆的大小。而堆的最大受限于系统 使用的物理内存。一般使用数据量比较大的 应用程序 会使用持久化对象,内存使用有可能迅速的增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示 内存溢出 ,并导致应用程序崩溃。因此一般建议堆的最大值设置为可用内存的最大值的 80%。
  说明:如果 -Xmx 不指定或指定偏小,应用可能会导致 java.lang.OutOfMemory错误,此错误来自 JVM,不是 Throwable的,无法用 try...catch 捕捉。
 
-XX:newSize,-XX:MaxnewSize,-Xmn :新生代,中生代,老生代。Java 中每 new一个对象所占用的内存空间就是 新生代的空间,当 Java 垃圾回收机制对堆区进行资源回收后,那些新生代中没有被回收的资源将被转移到中生代,中生代的被转移到老生代。
  1. -XX:newSize:表示新生代初始内存的大小,应该小于 -Xms的值,堆内存与新生代是包含关系。
  2. -XX:MaxnewSize:表示新生代可被分配的内存的最大上限;这个值应小于 -Xmx的值。
  3. -Xmn:这个参数则是对 -XX:newSize,-XX:MaxnewSize 两个参数的同时配置,也就是说如果通过 -Xmn来配置新生代的内存大小,那么 -XX:newSize = -XX:newMaxSize = -Xmn,虽然会很方便,但需要注意的是这个参数是在 JDK1.4 版本之后才使用的。
 
非堆区内存配置的两个参数:
  1. -XX:PermSize: 表示非堆区初始内存分配大小,其缩写为 Permanent Size(持久化内存)
  2. -XX:MaxPermSize:表示堆非堆区分配的内存的最大上限。
  这里需要注意一点:在配置之前一定要慎重的考虑一下自身软件所需要的非堆区内存大小,因为此处内存是不会被 Java垃圾回收机制处理的地方。并且更加要注意的是 最大堆内存与最大非堆内存的和绝对不能超出操作系统的可用内存。
XX:MaxPermSize 设置 过小会导致 java.lang.OutOfMemoryError:PermGen space 就是内存溢出。
为什么会内存溢出:
  a. 这一部分内存存放 Class 和 Meta 的信息,Class在被 Load的时候被放入 PermGen space 区域,它和存放 Instance的 Heap 区域不通。
  b. GC(Garbage Colloction)不会在主程序运行期对 PermGen space进行清理,所以如果 APP 会 Load 很多 Class的话,就很可能出现 PermGen space错误。
 

 
堆(Heap)和非堆(Non-heap)内存
  按照官方的说法:“Java虚拟机具有 一个堆,堆是运行时数据区域,所有类实例和数组的内存均从 此处分配。堆是在 Java虚拟机启动时创建的。” “在 JVM 中堆之外的内存称为非堆内存(Non-heap memory)”。
  可以看出 JVM主要管理两种类型的内存:堆 和 非堆。简单来说堆就是 Java代码可及的内存,是留给开发人员使用的;非堆就是 JVM留给自己用的。
  所以方法区、JVM内部处理或优化所需要的内存(如 JIT 编译后的代码缓存)、每个类结构(如 运行时 常数池,字段和方法数据)以及 方法和构造方法的代码都在非堆内存中。
 

 
-Xms 和 -Xmx 不能设置的太大
  通过在命令行中执行 Java或者启动某种基于 Java的中间件来运行 Java应用程序时,Java会创建一个操作系统进程。作为操作系统进程,Java运行时面临着与其他进程完全相同的内存限制:架构提供给的寻地址能力以及操作系统提供的用户空间。
  处理器架构提供的部分可寻址范围由 OS 本身占用,提供给操作系统内核及 C运行时(对于使用 C 或者 C++ 编写的 JVM而言)。OS 和 C运行时占用的内存数量取决于所用的 OS,但通过数量较大:Windows默认占用的内存是 2GB。剩余的可寻地址空间(用术语表示就是用户空间)就是可供运行的实际 进程的内存。
  对于 Java应用程序,用户空间是 Java进程占用的内存,实际上包含两个池:Java堆和本机(非Java)堆。Java堆的大小由 JVM的 Java堆设置控制: -Xms和 -Xmx分别设置最小和最大 Java堆。在按照最大的大小设置了Java堆之后,剩下的用户空间就是 本机堆。

 

  可循范围地址总共有 4GB,OS 和 C 运行时大约占用了其中 1GB,Java堆占用了将近 2GB,本机堆占用了其他部分。注意: JVM 本身也要占用内存,就像 OS内核和 C 运行时 一样,而 JVM占用的内存是 本机堆(非堆 )的子集。
  由此可见 -Xms 和 -Xmx设置的过大,那么 JVM的内存将被抢占,其他应用程序也无法运行。
 

 
Heap越大限制并发量!
  进程模型:x86的机器上的进程最多可以使用 2048 mb的内存
  假设 /** VM Args -Xmx 1024 **/ 那么
  该 Java进程剩下的内存由:方法区、程序计数器、虚拟机栈、本地方法栈 共同使用。
  虚拟机使用的空间 = 2048 - -Xmx(最大堆容量) - -MaxPermSize(最大方法区容量) - 本地方法栈

 

  我们知道,一个线程对应一个虚拟机栈
  所以总结:那么Heap越大,可以供程序申请的内存空间越少,也就是说虚拟机栈越少(线程数量越少)。 堆内存存储了对象,我们称为 GC堆 ,我们增加 -Xmx只是增加了 GC堆的大小,真正执行的内存空间反而更小了。
 
虚拟机栈图解
  如图:右边绿色是虚拟机栈(每个线程里面都是顺序 执行的 ,里面的所有的程序都是在栈帧里,若一段代码 调用了一个方法就会产生一个新的栈帧压到栈里去)。因此,一个方法我们可以理解为 一个栈帧。

 

使用 OOM Dump来调优
  1. 对于高并发,创建对象不多的项目,可以降低 Xmx的配置 ,结合 Xms设定堆 范围。
  2. 对于低并发,创建对象多的项目,可以适当提高 Xmx
  3. 因为对象和数组存放到 heap内的,栈帧中其实只存了对象地址,所以不存在爆的情况。
 

 
Tomcat调优
 
增加JVM堆内存大小
-Xms,-Xmx 这两个值的大小一般根据需要进行配置。
  初始化堆的大小执行了虚拟机在启动时向系统申请的内存大小。一般而言,这个参数不重言,但是有的应用程序在大负载的情况下会急剧的占用更多的内存,此时这个参数就是显得非常重要,如果虚拟机启动时设hi的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复的增加内存来满足使用。由于这种原因,我们一般把 -Xms和 -Xmx设置为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久化对象,内存使用有可能迅速增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此,一般建议堆的最大值设置为可用内存的最大值的 80%。
另外需要考虑的是 Java提供的垃圾回收机制 。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用 有关,应该通过分析实际的垃圾收集时间和频率来调整。如果堆的大小很小,那么垃圾收集就会很慢,但是频率会降低。如果把堆的大小和内存的需要一致,完全收集就会很快,但是会更加频繁。调整堆大小的目的时最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为了保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。
  如果系统花费很多的时间收集垃圾,请减少堆大小。一次完全的垃圾收集应该 不超过 3 - 5秒。如果垃圾收集称为瓶颈,那么需要指定代的大小,检查垃圾收集的详细输出,研究垃圾收集参数对性能的影响。一般来说,应该使用物理内存的 80% 作为堆大小。当处增加处理器时,记得增加 内存,因为分配可以并行进行,而垃圾收集不是并行的。
 
线程池配置
  线程池指定 Web 请求负载的数量。因此,为了获得更好的性能这部分要小心处理。可以通过调整连接器属性 “maxThreads”完成配置。 maxThreads的值应该根据流量的大小,如果值过低,没有足够的线程来处理所有的请求,请求将进入等待状态,只有当一个的线程释放后 才被处理;如果设置太大,Tomcat的启动将花费更多的时间。因此它取决于我们给 maxThreads 设置一个正确的值。
 
enableLookups="false" redirectPort="8181" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" />
  上述配置中,maxThreads 值设置为 “250”,这指定可以由服务器处理的并发请求的最大数量。如果没有指定,这个属性的默认值时 “200”. 任何多出的并发将收到拒绝连接的错误提示,直到另一个处理请求进程被释放。错误类似如下
  org.apache.tomcat.util.threads.ThreadPool logFull SEVERE: All threads (250) are currently busy, waiting. Increase maxThreads (250) or check the servlet status
  注意:如果请求的数量超过了 “750”,这将不是意味着将 maxThreads属性值设置为 “750”,它意味着最好使用 Tomcat集群的多个实例。也就是说,如果有 “1000”个请求,两个 Tomcat实例配置 “maxThreads=500”,而不是在单 Tomcat实例的情况下设置 maxThreads=1000。准确值的设定需要在各种环境下测试得出。
 
压缩
  Tomcat有一个通过在 server.xml 配置文件中设置压缩的选项。压缩可以在 connector像如下设置中完成:
 
  在前面的配置中,当文件的大小大于等于 500bytes时才会压缩。如果文件达到了大小却没有被压缩,那么设置属性 comperession=“on”。否则Tomcat默认设置时“off”。
 
数据库性能调优
  Tomcat性能在等待数据库查询被执行期间会降低。如今大多数应用都是 使用可能包含 “命名查询” 的关系型数据库。如果是那这样的话,Tomcat会在启动时默认加载命名查询,这个可能会提升性能。另一件重要事是确保所有数据库 连接正确的关闭。给数据库连接池设置值也是 十分重要的。我们所说的值是指 Resource 要素的最大空闲数(maxIdle),最大连接数(maxActive),最大建立连接等待时间(maxWait)属性 的值。这些需要分析查询语句的耗时与池相结合算出。
 
 
posted @ 2022-11-10 15:56  茄子777  阅读(275)  评论(0)    收藏  举报