除了java堆和永久代外,还有下面这些区域会占用较多内存:
1. Direct Memory
- Direct Memory内存不算在堆内存之中,只能在剩余的内存中分出一部分给它。- Direct Memory只能等待(老年代满了后)full gc时,顺便帮它清理一下。否则它只能等到内存溢出异常时,先catch掉,再执行System.gc(),如果虚拟机还是不听(如打开了-XX:+DisableExplicitGC开关),就只能眼睁睁地看着堆内还有很多内存,自己却不得不抛出内存溢出异常了。- 可以通过-XX: MAXDirectMemorySize调整大小。内存不足时,抛出OutOfMemoryError或者OutOfMemoryError: Direct buffer memory- 大量NIO操作可能会使用到Direct Memory
2. 线程堆栈
可以通过-Xxs调整大小,内存不足时抛出StackOverFlowError(纵向无法分配,即无法分配新的栈帧),或者OutOfMemoryError: unable to create new native thread(横向无法分配,即无法建立新的线程)
3. Socket缓存区
- 每个Socket连接都有Receive和Send两个缓存区,分别占大约37KB和25KB内存,连接多的话这块内存占用也比较可观。如果没法分配,则可能会抛出IOException: too many open files。- [捂脸]曾经做过一个网站,网站需要记录访问数,当时是把这些数据存在文件中的,但是打开的文件忘记关闭。每来一个新的访问者,文件都会被打开一次,当超出**限制**时,就会报too many open files。然后发现是Linux对打开的文件数量有限制,就将限制数量调整到最大,还是会报错。后来才发现是文件打开后没关,将它关闭后就好了。所以报这个错误的时候有可能是Socket连接数太多,也可能真的是文件打开的太多了。
4. JNI代码
如果代码中使用JNI调用本地库,那本地库使用的内存也不在堆中。
5. 虚拟机和GC
虚拟机、GC的代码执行也要消耗一定内存