Tomcat 運(yùn)行時(shí)的JVM調(diào)優(yōu)

前言

TOMCAT的性能直接取決于JVM,所以對(duì)于TOMCAT的性能調(diào)優(yōu)也大都集中在配置TOMCAT運(yùn)行時(shí)的JVM參數(shù)上

JVM內(nèi)存機(jī)制

JVM為java虛擬機(jī),使用來編譯執(zhí)行java代碼用。java內(nèi)存分為register,stack,heap,static storage,constant storage,permanent generation space.

Register

寄存器(register)。這是最快的存儲(chǔ)區(qū),因?yàn)樗挥诓煌谄渌鎯?chǔ)區(qū)的地方——處理器內(nèi)部。但是寄存器的數(shù)量極其有限,所以寄存器由編譯器根據(jù)需求進(jìn)行分配。你不能直接控制,也不能在程序中感覺到寄存器存在的任何跡象。

stack

位于通用 RAM(random-access memory,隨機(jī)訪問存儲(chǔ)器)中,但通
過它的“堆棧指針”可以從處理器那里獲得直接支持。堆棧指針若向下移動(dòng),則分配
新的內(nèi)存;若向上移動(dòng),則釋放那些內(nèi)存。這是一種快速有效的分配存儲(chǔ)方法,僅次
于寄存器。創(chuàng)建程序時(shí),Java 編譯器必須知道存儲(chǔ)在堆棧內(nèi)所有數(shù)據(jù)的確切大小和生
命周期,因?yàn)樗仨毶上鄳?yīng)的代碼,以便上下移動(dòng)堆棧指針。這一約束限制了程序
的靈活性,所以雖然某些 Java 數(shù)據(jù)存儲(chǔ)于堆棧中——特別是對(duì)象引用,但是 Java 對(duì)
象并不存儲(chǔ)于其中

heap

堆(heap)。一種通用性的內(nèi)存池(也存在于 RAM 區(qū)) ,用于存放所有的 Java 對(duì)象。
堆不同于堆棧的好處是:編譯器不需要知道要從堆里分配多少存儲(chǔ)區(qū)域,也不必知道
存儲(chǔ)的數(shù)據(jù)在堆里存活多長(zhǎng)時(shí)間。因此,在堆里分配存儲(chǔ)有很大的靈活性。當(dāng)你需要
創(chuàng)建一個(gè)對(duì)象時(shí),只需用 new 寫一行簡(jiǎn)單的代碼,當(dāng)執(zhí)行這行代碼時(shí),會(huì)自動(dòng)在堆里
進(jìn)行存儲(chǔ)分配。當(dāng)然,為這種靈活性必須要付出相應(yīng)的代價(jià)。用堆進(jìn)行存儲(chǔ)分配比用
堆棧進(jìn)行存儲(chǔ)存儲(chǔ)需要更多的時(shí)間 (如果確實(shí)可以在 Java 中像在 C++中一樣用棧保存
對(duì)象) 。

static storage

靜態(tài)存儲(chǔ)(static storage)
。這里的“靜態(tài)”是指“在固定的位置” (盡管也在 RAM 里)。
靜態(tài)存儲(chǔ)里存放程序運(yùn)行時(shí)一直存在的數(shù)據(jù)。你可用關(guān)鍵字 Static 來標(biāo)識(shí)一個(gè)對(duì)象的
特定元素是靜態(tài)的,但 Java 對(duì)象本身從來不會(huì)存放在靜態(tài)存儲(chǔ)空間里。

constant storage

常量存儲(chǔ)(constant storage)。常量值通常直接存放在程序代碼內(nèi)部,這樣做是安全的,
因?yàn)樗鼈冇肋h(yuǎn)不會(huì)被改變。有時(shí),在嵌入式系統(tǒng)中,常量本身會(huì)和其它部分隔離開,
所以在這種情況下,可以選擇將其存放在 ROM(read-only memory,只讀存儲(chǔ)器)中。

Permanent Generation Space

簡(jiǎn)單而言,便是加載類的區(qū)域,詳細(xì)介紹參見這里

總結(jié)

寄存器存放指令、棧存放對(duì)象引用、堆存放對(duì)象、PerGen Space加載和存放類

TOMCAT調(diào)優(yōu)

JVM會(huì)對(duì)heap和PerGen Space執(zhí)行垃圾回收,回收和分配空間本身都會(huì)消耗一定性能。所以TOMCAT調(diào)優(yōu)的核心就在于選擇哪一種模式的回收器,以及如何減少垃圾回收器執(zhí)行的次數(shù)。

具體點(diǎn)也就說:將堆內(nèi)存的初始化大小和極限大小指定相同的值,將類加載區(qū)的PerGen Space的初始大小和極限大小指定相同的值,這樣避免垃圾JVM頻繁的分配資源和垃圾回收。以及根據(jù)具體的系統(tǒng)配置選擇垃圾回收器的類型,比如并行、并發(fā)回收等。詳細(xì)內(nèi)容如下

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容