Tomcat 运行时的JVM调优

前言

TOMCAT的性能直接取决于JVM,所以对于TOMCAT的性能调优也大都集中在配置TOMCAT运行时的JVM参数上

JVM内存机制

JVM为java虚拟机,使用来编译执行java代码用。java内存分为register,stack,heap,static storage,constant storage,permanent generation space.

Register

寄存器(register)。这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。

stack

位于通用 RAM(random-access memory,随机访问存储器)中,但通
过它的“堆栈指针”可以从处理器那里获得直接支持。堆栈指针若向下移动,则分配
新的内存;若向上移动,则释放那些内存。这是一种快速有效的分配存储方法,仅次
于寄存器。创建程序时,Java 编译器必须知道存储在堆栈内所有数据的确切大小和生
命周期,因为它必须生成相应的代码,以便上下移动堆栈指针。这一约束限制了程序
的灵活性,所以虽然某些 Java 数据存储于堆栈中——特别是对象引用,但是 Java 对
象并不存储于其中

heap

堆(heap)。一种通用性的内存池(也存在于 RAM 区) ,用于存放所有的 Java 对象。
堆不同于堆栈的好处是:编译器不需要知道要从堆里分配多少存储区域,也不必知道
存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当你需要
创建一个对象时,只需用 new 写一行简单的代码,当执行这行代码时,会自动在堆里
进行存储分配。当然,为这种灵活性必须要付出相应的代价。用堆进行存储分配比用
堆栈进行存储存储需要更多的时间 (如果确实可以在 Java 中像在 C++中一样用栈保存
对象) 。

static storage

静态存储(static storage)
。这里的“静态”是指“在固定的位置” (尽管也在 RAM 里)。
静态存储里存放程序运行时一直存在的数据。你可用关键字 Static 来标识一个对象的
特定元素是静态的,但 Java 对象本身从来不会存放在静态存储空间里。

constant storage

常量存储(constant storage)。常量值通常直接存放在程序代码内部,这样做是安全的,
因为它们永远不会被改变。有时,在嵌入式系统中,常量本身会和其它部分隔离开,
所以在这种情况下,可以选择将其存放在 ROM(read-only memory,只读存储器)中。

Permanent Generation Space

简单而言,便是加载类的区域,详细介绍参见这里

总结

寄存器存放指令、栈存放对象引用、堆存放对象、PerGen Space加载和存放类

TOMCAT调优

JVM会对heap和PerGen Space执行垃圾回收,回收和分配空间本身都会消耗一定性能。所以TOMCAT调优的核心就在于选择哪一种模式的回收器,以及如何减少垃圾回收器执行的次数。

具体点也就说:将堆内存的初始化大小和极限大小指定相同的值,将类加载区的PerGen Space的初始大小和极限大小指定相同的值,这样避免垃圾JVM频繁的分配资源和垃圾回收。以及根据具体的系统配置选择垃圾回收器的类型,比如并行、并发回收等。详细内容如下

At this point we've covered the basics and are ready to begin examining the JVM options that are available to us. Please note that while these options can be used for any application running on the JVM, this article will focus sole only how they can be applied to Tomcat. The usage of these options for other applications may or may not be appropriate.

Note: For simplicity, it is assumed that you are running an Oracle Hotspot JVM.

General Options

-Xms and -Xmx

These settings are used to define the size of the heap used by the JVM. -Xms defines the initial size of the heap and -Xmx defines the maximum size of the heap. Specific values for these options will depend on the number of applications and the requirements of each application deployed to a Tomcat instance.

With regard to Tomcat, it is recommended that the initial and maximum values for heap size be set to the same value. This is often referred to as a fully committed heap and this will instruct the JVM to create a heap that is initially at its maximum size and prevent several full garbage collections from occurring as the heap expands to its maximum size.

-XX:PermSize and -XX:MaxPermSize

These settings are used to define the size of the permanent generation space. -XX:PermSizedefines the initial value and -XX:MaxPermSize defines the maximum value.

With regard to Tomcat, it is recommended that the initial and maximum values for the size of the permanent generation be set to the same value. This will instruct the JVM to create the permanent generation so that it is initially at its maximum size and prevent possible full garbage collections from occurring as the permanent generation expands to its maximum size.

At this point, you might be thinking that this seems awful similar to the -Xms and -Xmx options, and while the concept is the same, “PermGen” or permanent generation, refers to the location in memory where the JVM stores the class files that have been loaded into memory. This is different and distinct from the heap (specified by -Xms and -Xmx) which is where the JVM stores the object instances used by an application.

One final note, if the PermGen space becomes full (regardless of the availability of memory in the heap) then the JVM will attempt a full garbage collection to reclaim space. This can often be a source of problems for applications which dynamically create or load a large number of classes. Proper sizing of -XX:PermSize and -XX:MaxPermSize for your applications will allow you to work around this issue.

-Xss

This setting determines the size of the stack for each thread in the JVM. The specific value that you should use will vary depending on the requirements of the applications deployed to Tomcat, however in most cases the default value used by the JVM is too large.

For a typical installation, this value can be lowered, saving memory and increasing the number of threads that can be run on a system. The easiest way to determine a value for your system is to start out with a very low value, for example 128k. Then run Tomcat and look for a StackOverFlow exception in the logs. If you see the exception, then gradually increase the value and restart Tomcat. When the exceptions disappear, you have found the minimal value which works for your deployment.

-server

This setting will select the Java HotSpot Server VM. This will instruct the VM that it is running in a server environment and the default configurations will be changed accordingly.

Note, this option is really only needed when running 32-bit Windows, as 32-bit Solaris and 32-bit Linux installations with two or more CPU's and 2GB or more of RAM will enable this option by default. In addition, all 64-bit OS's have this option enabled by default as there is no 64-bit client VM.

For a comprehensive list of JVM options, please see the article Java HotSpot VM Options.

Selecting a Collector

For many users, tuning the basic options I mentioned in the previous section will be sufficient for their applications. However, for larger applications or applications which just require larger heap sizes these options may not be sufficient. If your Tomcat installation fits this profile then you'll want to take one further step and tune the collector.

To begin tuning the collector, you need to pick the right collector for your application. The JVM ships with three commonly used collectors: the serial collector, the parallel collector and the concurrent collector. In most cases when running Tomcat, you'll be using either the parallel collector or the concurrent collector. The difference between the two being that the parallel collector typically offers the better throughput, while the concurrent collector often offers lower pause times.

The parallel collector can be enabled by adding -XX:+UseParallelGC to CATALINA_OPTS or the concurrent collector can be enabled by adding -XX:+UseConcMarkSweepGC to CATALINA_OPTS (you would never want to have both options enabled). As to which of the collectors you should be using, it is difficult to give a blanket recommendation. I would suggest that you give both a try, measure the results and use that to make your decision.

Once you have selected a collector, it is possible to take one further step and apply some configuration settings which are specific to the collector. That being said, most of the time the JVM will detect and set excellent values for these options. You should not attempt to manually configure these unless you have a good understanding of how the specific garbage collector is working, you are applying rule number one from above and you really know what you are doing. That said, I'm going to talk about two options, one for the parallel collector and one for the concurrent collector.

When you specify the option to run the parallel collector, it will only run on the young generation. This means that multiple threads will be used to process the young generation, but the old generation will continue to be processed by a single thread. To enable parallel compaction of the old generation space you can enable the option -XX:+UseParallelOldGC. Note that this option will help the most when enabled on a system with many processors.

When you specify the option to run the concurrent collector, it is important to realize that garbage collection will happen concurrently with the application. This means that garbage collection will consume some of the processor resources that would have otherwise been available to the application. On systems with a large number of processors, this is typically not a problem. However, if your system has only one or two processors then you will likely want to enable the -XX:+CMSIncrementalMode option. This option enables incremental mode for the collector, which instructs the collector to periodically yield the processor back to the application and essentially prevents the collector from running for too long.

Conclusion

Congratulations! If you're reading this then you made it all the way through the article. If you're still a little confused at this point, that is OK and I would say normal. Tuning the JVM and the inner workings of the garbage collectors is not an easy subject.

If you'd like to get some additional information on the subject, I would strongly suggest continuing on with this article from Oracle, Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning. It is very technical and should be able to fill in any of the gaps left from this article.

参考资料地址:

http://www.tomcatexpert.com/blog/2011/11/22/performance-tuning-jvm-running-tomcat

https://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation

你可能感兴趣的:(Tomcat 运行时的JVM调优)