java面试必备--JVM篇(四)

     相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499!
 


目录

1 高手总结的9种 OOM 常见原因及解决方案

1、Java heap space

2、GC overhead limit exceeded

3、Permgen space

4、Metaspace

         5、Unable to create new native thread

6、Out of swap space?

7、 Kill process or sacrifice child

8、Requested array size exceeds VM limit

9、Direct buffer memory

2 JVM对并发分配内存处理方式

3 GC新生代对象晋升到老年代情况总结

4 Java出现OutOfMemoryError(OOM)的原因有那些?出现OOM错误后,怎么解决?

5 关于Object o = new Object()的追魂8连问!

 1. 解释一下对象的创建过程。(半初始化)

2. DCL单例是否需要加volatile?(指令重排)

3. 对象在内存中的存储布局。(普通对象与数组的存储结构区别)

4. 对象头的组成(markword等)

5. 对象如何定位

6 对象怎么分配

7. Object o = new Object()在内存中占用多少字节?

8. Class对象是在堆还是在方法区?


1 高手总结的9种 OOM 常见原因及解决方案

      当 JVM 内存严重不足时,就会抛出 java.lang.OutOfMemoryError 错误。本文总结了常见的 OOM 原因及其解决方法,如下图所示。如有遗漏或错误,欢迎补充指正。

1、Java heap space

    当堆内存(Heap Space)没有足够空间存放新创建的对象时,就会抛出 java.lang.OutOfMemoryError:Javaheap space 错误(根据实际生产经验,可以对程序日志中的 OutOfMemoryError 配置关键字告警,一经发现,立即处理)。

原因分析

 Javaheap space 错误产生的常见原因可以分为以下几类:

        1、请求创建一个超大对象,通常是一个大数组。

        2、超出预期的访问量/数据量,通常是上游系统请求流量飙升,常见于各类促销/秒杀活动,可以结合业务流量指标排查是否有尖状峰值。

        3、过度使用终结器(Finalizer),该对象没有立即被 GC。

        4、内存泄漏(Memory Leak),大量对象引用没有释放,JVM 无法对其自动回收,常见于使用了 File 等资源没有回收。

解决方案

       针对大部分情况,通常只需要通过 -Xmx 参数调高 JVM 堆内存空间即可。如果仍然没有解决,可以参考以下情况做进一步处理:

      1、如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全部结果,而没有做结果数限制。

      2、如果是业务峰值压力,可以考虑添加机器资源,或者做限流降级。

      3、如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接。

2、GC overhead limit exceeded

      当 Java 进程花费 98% 以上的时间执行 GC,但只恢复了不到 2% 的内存,且该动作连续重复了 5 次,就会抛出 java.lang.OutOfMemoryError:GC overhead limit exceeded 错误。简单地说,就是应用程序已经基本耗尽了所有可用内存, GC 也无法回收。

     此类问题的原因与解决方案跟 Javaheap space 非常类似,可以参考上文。

3、Permgen space

      该错误表示永久代(Permanent Generation)已用满,通常是因为加载的 class 数目太多或体积太大。

原因分析

      永久代存储对象主要包括以下几类:

      1、加载/缓存到内存中的 class 定义,包括类的名称,字段,方法和字节码;

      2、常量池;

      3、对象数组/类型数组所关联的 class;

      4、JIT 编译器优化后的 class 信息。

      PermGen 的使用量与加载到内存的 class 的数量/大小正相关。

解决方案

 根据 Permgen space 报错的时机,可以采用不同的解决方案,如下所示:

       1、程序启动报错,修改 -XX:MaxPermSize 启动参数,调大永久代空间。

       2、应用重新部署时报错,很可能是没有应用没有重启,导致加载了多份 class 信息,只需重启 JVM 即可解决。

      3、运行时报错,应用程序可能会动态创建大量 class,而这些 class 的生命周期很短暂,但是 JVM 默认不会卸载 class,可以设置 -XX:+CMSClassUnloadingEnabled 和 -XX:+UseConcMarkSweepGC这两个参数允许 JVM 卸载 class。

   如果上述方法无法解决,可以通过 jmap 命令 dump 内存对象 jmap-dump:format=b,file=dump.hprof<process-id> ,然后利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析开销最大的 classloader 和重复 class。

4、Metaspace

     JDK 1.8 使用 Metaspace 替换了永久代(Permanent Generation),该错误表示 Metaspace 已被用满,通常是因为加载的 class 数目太多或体积太大。

      此类问题的原因与解决方法跟 Permgenspace 非常类似,可以参考上文。需要特别注意的是调整 Metaspace 空间大小的启动参数为 -XX:MaxMetaspaceSize

5、Unable to create new native thread

       每个 Java 线程都需要占用一定的内存空间,当 JVM 向底层操作系统请求创建一个新的 native 线程时,如果没有足够的资源分配就会报此类错误。

原因分析

      JVM 向 OS 请求创建 native 线程失败,就会抛出 Unableto createnewnativethread,常见的原因包括以下几类:

     1、线程数超过操作系统最大线程数 ulimit 限制;

     2、线程数超过 kernel.pid_max(只能重启);

     3、native 内存不足;

该问题发生的常见过程主要包括以下几步:

    1、JVM 内部的应用程序请求创建一个新的 Java 线程;

    2、JVM native 方法代理了该次请求,并向操作系统请求创建一个 native 线程;

    3、操作系统尝试创建一个新的 native 线程,并为其分配内存;

    4、如果操作系统的虚拟内存已耗尽,或是受到 32 位进程的地址空间限制,操作系统就会拒绝本次 native 内存分配;

    5、JVM 将抛出 java.lang.OutOfMemoryError:Unableto createnewnativethread 错误。

解决方案

      1、升级配置,为机器提供更多的内存;

      2、降低 Java Heap Space 大小;

      3、修复应用程序的线程泄漏问题;

      4、限制线程池大小;

      5、使用 -Xss 参数减少线程栈的大小;

      6、调高 OS 层面的线程最大数:执行 ulimia-a 查看最大线程数限制,使用 ulimit-u xxx 调整最大线程数限制。

ulimit -a .... 省略部分内容 ..... max user processes (-u) 16384

6、Out of swap space?

      该错误表示所有可用的虚拟内存已被耗尽。虚拟内存(Virtual Memory)由物理内存(Physical Memory)和交换空间(Swap Space)两部分组成。当运行时程序请求的虚拟内存溢出时就会报 Outof swap space? 错误。

原因分析

该错误出现的常见原因包括以下几类:

      1、地址空间不足;

      2、物理内存已耗光;

      3、应用程序的本地内存泄漏(native leak),例如不断申请本地内存,却不释放。

      4、执行 jmap-histo:live<pid> 命令,强制执行 Full GC;如果几次执行后内存明显下降,则基本确认为 Direct ByteBuffer 问题。

解决方案

    根据错误原因可以采取如下解决方案:

    1、升级地址空间为 64 bit;

    2、使用 Arthas 检查是否为 Inflater/Deflater 解压缩问题,如果是,则显式调用 end 方法。

    3、Direct ByteBuffer 问题可以通过启动参数 -XX:MaxDirectMemorySize 调低阈值。

    4、升级服务器配置/隔离部署,避免争用。

7、 Kill process or sacrifice child

      有一种内核作业(Kernel Job)名为 Out of Memory Killer,它会在可用内存极低的情况下“杀死”(kill)某些进程。OOM Killer 会对所有进程进行打分,然后将评分较低的进程“杀死”,具体的评分规则可以参考 Surviving the Linux OOM Killer。

      不同于其他的 OOM 错误, Killprocessorsacrifice child 错误不是由 JVM 层面触发的,而是由操作系统层面触发的。

原因分析

      默认情况下,Linux 内核允许进程申请的内存总量大于系统可用内存,通过这种“错峰复用”的方式可以更有效的利用系统资源。

      然而,这种方式也会无可避免地带来一定的“超卖”风险。例如某些进程持续占用系统内存,然后导致其他进程没有可用内存。此时,系统将自动激活 OOM Killer,寻找评分低的进程,并将其“杀死”,释放内存资源。

解决方案

     1、升级服务器配置/隔离部署,避免争用。

     2、OOM Killer 调优。

8、Requested array size exceeds VM limit

       JVM 限制了数组的最大长度,该错误表示程序请求创建的数组超过最大长度限制。

    JVM 在为数组分配内存前,会检查要分配的数据结构在系统中是否可寻址,通常为 Integer.MAX_VALUE-2

     此类问题比较罕见,通常需要检查代码,确认业务是否需要创建如此大的数组,是否可以拆分为多个块,分批执行。

9、Direct buffer memory

     Java 允许应用程序通过 Direct ByteBuffer 直接访问堆外内存,许多高性能程序通过 Direct ByteBuffer 结合内存映射文件(Memory Mapped File)实现高速 IO。

原因分析

      Direct ByteBuffer 的默认大小为 64 MB,一旦使用超出限制,就会抛出 Directbuffer memory 错误。

解决方案

     1、Java 只能通过 ByteBuffer.allocateDirect 方法使用 Direct ByteBuffer,因此,可以通过 Arthas 等在线诊断工具拦截该方法进行排查。

     2、检查是否直接或间接使用了 NIO,如 netty,jetty 等。

     3、通过启动参数 -XX:MaxDirectMemorySize 调整 Direct ByteBuffer 的上限值。

    4、检查 JVM 参数是否有 -XX:+DisableExplicitGC 选项,如果有就去掉,因为该参数会使 System.gc() 失效。

  5、检查堆外内存使用代码,确认是否存在内存泄漏;或者通过反射调用 sun.misc.Cleaner 的 clean() 方法来主动释放被 Direct ByteBuffer 持有的内存空间。

    6、内存容量确实不足,升级配置。

推荐工具&产品

JVM 内存分析工具 mat

       1、Eclipse Memory Analyzer

        https://www.eclipse.org/mat

阿里云 APM 产品,支持 OOM 异常关键字告警

       2、ARMS

https://help.aliyun.com/document_detail/42966.html?spm=a2c4g.11174283.6.685.d69b668cuztvff

阿里 Java 在线诊断工具 Arthas(阿尔萨斯)

       3、alibaba Arthas

https://github.com/alibaba/arthas

JVM对并发分配内存处理方式

       1:对进行分配内存的动作进行同步处理-实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。

      2:把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程预先在JAVA堆中预先分配一小块内存,称为本地线程分配缓存(TLAB)。

      哪个线程要分配内存,就在哪个线程的TLAB上进行分配。只有在TLAB用完并分配新的TLAB时,才需要加同步锁。虚拟机是否使用TLAB,可以通

      过参数-XX:+/UseTLAB参数来设定。

      3:直接在栈上分配,如果对象没有发生逃逸,那么对象将不会再堆上分配。对象随线程的销毁而销毁,垃圾回收的时间少,性能高,吞吐量高,响应时间也能提高。

3 GC新生代对象晋升到老年代情况总结

      对象优先在Eden分配,且新生代对象晋升到老年代有多种情况,

现在做一个总结:

  1. Eden区满时,进行Minor GC,当Eden和一个Survivor区中依然存活的对象无法放入到Survivor中,则通过分配担保机制提前转移到老年代中。
  2. 若对象体积太大, 新生代无法容纳这个对象,-XX:PretenureSizeThreshold即对象的大小大于此值, 就会绕过新生代, 直接在老年代分配, 此参数只对Serial及ParNew两款收集器有效。
  3. 长期存活的对象将进入老年代。
  4. 虚拟机对每个对象定义了一个对象年龄(Age)计数器。当年龄增加到一定的临界值时,就会晋升到老年代中,该临界值由参数:-XX:MaxTenuringThreshold来设置。
  5. 如果对象在Eden出生并在第一次发生MinorGC时仍然存活,并且能够被Survivor中所容纳的话,则该对象会被移动到Survivor中,并且设Age=1;以后每经历一次Minor GC,该对象还存活的话Age=Age+1。
  6. 动态对象年龄判定。
  7. 虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,如果在Survivor区中相同年龄(设年龄为age)的对象的所有大小之和超过Survivor空间的一半,年龄大于或等于该年龄(age)的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。

Java出现OutOfMemoryError(OOM)的原因有那些?出现OOM错误后,怎么解决?

      触发 java.lang.OutOfMemoryError:最常见的原因就是应用程序需要的堆空间是大的,但是 JVM 提供的却小。这个的解决方法就是提供大的堆空间即可。除此之外还有复杂的原因:

      内存泄露:特定的编程错误会导致你的应用程序不停的消耗更多的内存,每次使用有内存泄漏风险的功能就会留下一些不能被回收的对象到堆空间中,随着时间的推移,泄漏的对象会消耗所有的堆空间,最终触发java.lang.OutOfMemoryError: Java heap space错误。

解决方案:

       第一个解决方案是显而易见的,你应该确保有足够的堆空间来正常运行你的应用程序,在JVM的启动配置中增加

如下配置:

-Xmx1024m

       流量/数据量峰值:应用程序在设计之初均有用户量和数据量的限制,某一时刻,当用户数量或数据量突然达到一个峰值,并且这个峰值已经超过了设计之初预期的阈值,那么以前正常的功能将会停止,并触发 java.lang.OutOfMemoryError: Java heap space异常

       解决方案,如果你的应用程序确实内存不足,增加堆内存会解决GC overhead limit问题,就如下面这样,给你的应用程序1G的堆内存:

java -Xmx1024m com.yourcompany.YourClass

5 关于Object o = new Object()的追魂8连问!

 1. 解释一下对象的创建过程。(半初始化)

 汇编码解释:

        (1) new #2 <T>:在内存中开辟一块空间并生成所有的成员变量,给t这个对象专用,此时成员变量m也有了,只是值是初始默认值0(long类型也是0;boolean类型是false;引用类型就是null)。

        (2) invokespecial #3 <T.<init>>:调用T类的构造方法,并给m赋值为8。

        (3) astore_1:将t与开辟的内存空间建立关联。

        如果当发生指令重排后,赋值指令与建立关联的指令交换顺序,使得后面来的线程在做判断时,发现对象已经创建,但是却不知道这个对象只是一个半初始状态,就直接拿来用,也就是拿到的对象m值并不是8而是默认值0,所以就下面这个问题。

2. DCL单例是否需要加volatile?(指令重排)

        双重检查的单例模式中加volatile是为了防止指令重排,与线程间可见的作用没有关系。如果不加volatile修饰实例对象,有可能以为指令重排序导致读取到一个半初始化状态的对象,也就是还没有完全赋值的对象,从而引发系统的业务逻辑等错误。

3. 对象在内存中的存储布局。(普通对象与数组的存储结构区别)

mack word:存放琐标志位、偏向锁位、线程ID、分代年龄等内容,下面详细解读。

        Klass word:也就是calss pointer,存储对象所属类的地址,就是为了标记到底是什么类的实例。jvm默认开启了指针压缩,所以占用4个字节,如果关闭指针压缩,就占用8个字节。此外,指针压缩还会影响instance data的实例对象的指针空间占用大小。如果开启了指针压缩,Long型的成员变量和long型的成员变量占用空间大小是有区别的:Long占用4个字节;long是基础类型占用8个字节。如果关闭了指针压缩:Long占用8个字节;long是基础类型占用8个字节。

        instance data:保存成员变量实例对象。

        padding:是为了保证整个内容加起来能被8个字节(Byte)整除而填充的空间,JVM读数据是一块一块读的,这样做效率是最高的。

        length:如果对象是数组,需要存储数组长度。

        详细解释请看我的往期文章《我的并发编程(二):java对象头以及synchronized升级过程》,里面描述得非常清楚。

4. 对象头的组成(markword等)

 详细解释:

        (1) 当我们创建一个无锁态对象的时候:25位没有用;31位装的identity Hashcode,但是只有在被调用的时候,才填充,没有调用的时候是空的;1位没有使用的;4位分代年龄(解释在下面);1位偏向锁位;2位锁标志位。

        (2) 偏向锁的时候:54位存下当前线程的ID;2位存批量撤销Epoch;1位没有使用;4位分代年龄;1位偏向锁位;2位锁标志位。

        (3) 自旋锁:62位指向线程中的Lock Record的指针。Lock Record与锁重入有关,synchronize默认是可重入的。自旋锁在竞争锁的时候,会在自己的内存的线程栈中创建一个Lock Record对象,抢到锁对象的资源时,锁对象头存的就是这个线程的Lock Record对象的指针,所以在重入的时候,会再创建一个Lock Record对象,利用Lock Record来记录到底琐了多少次。解锁的时候,就将一个Lock Record移除,移除的方式是FILO,也就是先进后出的原则。

        (4) 重量级琐:重量级琐是在C++代码层面进行的,会生成一个ObjectMonitor对象,这个对象中记录了一系列的队列,如下图:

        

         (5) 分代年龄:JVM有10种垃圾回收器,前面7种都涉及分代年龄,采用分代算法。当我们创建一个对象的时候,把它放在年轻代中,每经过一次垃圾回收后年龄就+1,也就是垃圾回收无法回收掉这个对象,它的年龄就会不断的增长,到达15,因为4个字节最大为15,就转到老龄代,年轻代的回收就不再对它进行回收。

        (6) hashCode部分:对象头上的hashCode并不是我们调用重写的hashCode()方法生成的,而是为重写的hashCode()方法或者调用System.identityHashcode()方法才能获取并且存入对象头中。通俗来讲,这里的hashCode是按照原始内容计算的,重写过的hashCode()方法计算的结果并不会存在此处。如果对象没有重写hashCode()方法,那么默认调用的os::random产生hashCode,也可以通过System.identityHashcode()获取。os::random产生hashCode的规则是:next_rand = (16807seed)mod(2*31-1),因此可以使用31位存储空间进行存储,并且一旦产生这个hashCode,JVM就会记录在markword中。

    详细解释请看我的往期文章《我的并发编程(二):java对象头以及synchronized升级过程》,里面描述得非常清楚。

5. 对象如何定位

    JVM中对象访问定位两种方式:

        (1) 直接指针访问:Java栈直接与对象进行访问,在Java堆中对象帆布中必须考虑存储访问类型的数据的相关信息 ,如下图:

      直接指针访问的优点比较明显,就是访问速度快,不需要和句柄一样指针定位的开销 。缺点也比较明显,就是对象在GC过程中,在新生代区域复制移动时,会比较麻烦。

        (2) 通过句柄方式访问:在Java堆中分出一块内存进行存储句柄池,在栈中存储的是句柄的地址,如下图:

     通过句柄访问有独特的优点,就是当对象移动的时候(垃圾回收的时候移动很普遍),这样值需要改变句柄中的指针,但是栈中的指针不需要变化,因为栈中存储的是句柄的地址。那么对应的缺点就是需要两次指针转换进行访问,访问速度比直接指针访问稍慢一些。 

6 对象怎么分配

    堆内存逻辑分区图如下:

 

     对象分配过程如下图:

 过程分析:

        (1) 当我们new出一个对象,JVM会首先尝试往栈上分配,如果能够分配得下,就分配到栈上分配到栈上的对象有好处就是不需要GC进行管理,什么时候不需要用到此对象了,将对象出栈就可以了。但是分配到栈上的对象是有要求的:第一,对象比较小,因为栈空间本来就不够大;第二,对象比较简答。

        (2) 如果栈上分配不下,我们就判断这个对象是不是够大,如果足够大就直接放在老年代区,在老年代区的对象经过一次全量垃圾回收FGC后,才有可能被回收掉。

        (3) 如果如果栈上分配不下并且对象不大,就会判断对象能否被存在线程本地分配缓冲区-TLAB(Thread Local Allocation Buffer)。但是不管放不放得下,都是放在新生代区的伊甸区eden。 但是因为堆是共享的,多个线程可以同时创建对象就可能会争夺同一块内存区域,所以为了保证线程安全,Eden区又被分配成一个个线程本地分配缓冲区,这个TLAB是线程私有的,每个线程都有自己的TLAB,避免了多线程环境下使用同步技术带来的性能损耗。

        (4) 伊甸区eden的对象在经过一次GC后,如果被回收掉了,那就结束了生命周期。

        (5) 伊甸区eden的对象在经过一次GC后,如果没有被回收掉,会被拷贝到幸存者区survivor1,对比上面的堆内存逻辑分区图。幸存者区survivor1中的对象再经过一次GC后如果对象还存活,那么就拷贝到幸存者区survivor2并且清理掉幸存者区survivor1中的所有对象,再有GC就反复这个操作,直到对象的分代年龄达到了移到老年代的界限(一般默认是15),就会被移到老年代中。

7. Object o = new Object()在内存中占用多少字节?

    这里考察的知识点是第3点对象在内存中的存储布局结构和类指针以及普通对象指针的概念。存储布局不再多说,类指针就是存储布局中的class pointer,普通对象指针就是存储布局中的instance data中,成员变量如果不是基础类型而是引用类型,那么也会有普通对象指针指向所属类。默认情况下JVM是开启了类指针和普通对象指针的指针压缩,将8个字节压缩成了4个字节。我们用代码输出来观察对象的大小,实验代码如下:

    (1) 默认开启所有指针压缩的情况下输出如下: 

 

 (2) 关闭类指针压缩后,如下:

 

  结论:针对这道题目,如果不考虑o这个对象引用本身,那就需要说清楚默认情况以及关闭类指针压缩后存储布局结构中的变化。

8. Class对象是在堆还是在方法区?

方法区,详见低5点对象如何定位的配图。

  1. 垃圾收集器整理&Java8默认垃圾回收器

垃圾收集器整理

1.Serial 垃圾收集器(单线程、复制算法)

2.ParNew 垃圾收集器(Serial+多线程)

3.Parallel Scavenge 收集器(多线程复制算法、高效)
自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个
重要区别。

4.Serial Old 收集器(单线程标记整理算法 )

5.Parallel Old 收集器(多线程标记整理算法)

6.CMS 收集器(多线程标记清除算法)
最主要目标是获取最短垃圾回收停顿时间

7.G1 收集器
相比与 CMS 收集器,G1 收集器两个最突出的改进是:

  1. 基于标记-整理算法,不产生内存碎片。
  2. 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

问题: java默认垃圾回收器是什么?

1.java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
2.jps
81413 SgupPlatformApplication
81412 Launcher
80585 Launcher
81433 Jps
80541 
80574 Launcher
3.jinfo -flag UseParallelOldGC 81413(进程号)
-XX:+UseParallelOldGC

那为什么不是Parallel Scavenge +Serial Old组合?
原因是:如果指定了-XX:+UseParallelGC参数,并行压缩默认是启用的。可以使用-XX:-UseParallelOldGC来禁用该功能。 也就是说当指定了参数-XX:+UseParallelGC,则默认也指定了-XX:+UseParallelOldGC。即默认使用了 Parallel old垃圾收集器。

总结: java8默认使用的应该是 Parallel Scavenge + Parallel Old

1 哪里设置JVM参数才起效?

大家都遇到过Java的 Outofmemory error,都知道要设置JVM的参数以及怎么设置,但是很少有人晓得去哪里设置才能起效,才能真正的应用到项目里,尤其是用惯了靠eclipse等开发工具等童鞋,这里讲解下怎么设置才有效的问题,以解除大家的烦恼。

设置JVM内存的参数有四个:

-Xmx    Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定;

-Xms    Java Heap初始值,Server端JVM最好将-Xms和-Xmx设为相同值,开发测试机JVM可以保留默认值;

-Xmn    Java Heap Young区大小,不熟悉最好保留默认值;

-Xss    每个线程的Stack大小,不熟悉最好保留默认值;

2. 如何设置JVM内存分配:

  (1)当在命令提示符下启动并使用JVM时(只对当前运行的类Test生效)

java -Xmx128m -Xms64m -Xmn32m -Xss16m Test

 (2)当在集成开发环境下(如eclipse)启动并使用JVM时

     a. 在eclipse根目录下打开eclipse.ini,默认内容为(这里设置的是运行当前开发工具的JVM内存分配):

-vmargs  -Xms40m  -Xmx256m 

-vmargs表示以下为虚拟机设置参数,可修改其中的参数值,也可添加-Xmn,-Xss,另外,eclipse.ini内还可以设置非堆内存,如:-XX:PermSize=56m,-XX:MaxPermSize=128m。

此处设置的参数值可以通过以下配置在开发工具的状态栏显示:

在eclipse根目录下创建文件options,文件内容为:org.eclipse.ui/perf/showHeapStatus=true

修改eclipse根目录下的eclipse.ini文件,在开头处添加如下内容:

-debug  options  -vm  javaw.exe 

重新启动eclipse,就可以看到下方状态条多了JVM信息。

      b. 打开eclipse-窗口-首选项-Java-已安装的JRE(对在当前开发环境中运行的java程序皆生效)

编辑当前使用的JRE,在缺省VM参数中输入:-Xmx128m -Xms64m -Xmn32m -Xss16m

    c. 打开eclipse-运行-运行-Java应用程序(只对所设置的java类生效)

选定需设置内存分配的类-自变量,在VM自变量中输入:-Xmx128m -Xms64m -Xmn32m -Xss16m

注:如果在同一开发环境中同时进行了b和c设置,则b设置生效,c设置无效,如:

开发环境的设置为:-Xmx256m,而类Test的设置为:-Xmx128m -Xms64m,则运行Test时生效的设置为:

-Xmx256m -Xms64m

 (3)当在服务器环境下(如Tomcat)启动并使用JVM时(对当前服务器环境下所以Java程序生效)

      a. 设置环境变量:

     变量名:CATALINA_OPTS

     变量值:-Xmx128m -Xms64m -Xmn32m -Xss16m

     b. 打开Tomcat根目录下的bin文件夹,编辑catalina.bat,将其中的%CATALINA_OPTS%(共有四处)替换为:-Xmx128m -Xms64m -Xmn32m -Xss16m

查看设置JVM内存信息

      Runtime.getRuntime().maxMemory();  //最大可用内存,对应-Xmx

      Runtime.getRuntime().freeMemory();  //当前JVM空闲内存

      Runtime.getRuntime().totalMemory();  //当前JVM占用的内存总数,其值相当于当前JVM已使用的内存及freeMemory()的总和

      关于maxMemory(),freeMemory()和totalMemory():

      maxMemory()为JVM的最大可用内存,可通过-Xmx设置,默认值为物理内存的1/4,设值不能高于计算机物理内存;

      totalMemory()为当前JVM占用的内存总数,其值相当于当前JVM已使用的内存及freeMemory()的总和,会随着JVM使用内存的增加而增加;

freeMemory()为当前JVM空闲内存,因为JVM只有在需要内存时才占用物理内存使用,所以freeMemory()的值一般情况下都很小,而JVM实际可用内存并不等于freeMemory(),而应该等于maxMemory()-totalMemory()+freeMemory()。及其设置JVM内存分配。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值