- 浏览: 620297 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
kongqinglong:
我艹,不好使,大骗子
基于Eclipse的FindBugs中文插件发布了 -
worket123:
误人子弟,不会就不要乱发
基于Eclipse的FindBugs中文插件发布了 -
accphc:
策略工厂实现Spring的ApplicationContext ...
Spring与策略模式 -
老凯和他的Java:
我也一直不漏的看完了,感触颇深,还是要多花花时间陪陪父母
纪念一位伟大的女性 -
IT_jingying:
认真的看完了,每一位母亲都是伟大的,她为自己的子女,家庭付出的 ...
纪念一位伟大的女性
JAVA的内存分配机制,在很多地方都已经解析很多次了,个人如何方便的来直观的了解,还有很多人不是很清楚,或者没有这样的机会,在这里我结合一个小例子,采用JDK自带的JConsole来说一下JVM的内存分配机制。
案例
首先解释下场景,服务端是一个通信服务器,接受客户端发过来的通信信息,并做业务处理;服务端采用JAVA中的MINA2框架,客户端可以任意,C++也好,JAVA也好,只要符合服务端规定的消息结构,发给通信服务器都能处理。
为了让大家更清楚,可以用MINA2框架中的时间服务器的例子来稍作修改模拟这个场景。系统环境,APACHE MINA2.0.4 + JDK1.6 + Eclipse3.7。
服务端代码如下:
业务处理器(这里当作一个时间服务器,接受到信息之后就立刻返回服务器当前时间)
客户端JAVA代码:
客户端处理类:
代码准备好之后,启动通信服务器服务端,然后启动客户端;
然后在打开你的jconsole界面,选择需要观察的那个服务端的JAVA进程;
首页就是类似这样的一个界面(注意:这篇文章里面所有的截图都是在模拟程序运行5个小时以后截下来的):
JAVA内存模型简介
我们知道:JAVA的内存模型是这样的,简单的来说,可以分为堆和非堆内存,下面的著名的这个图大家经常会看到:
内存由 Perm 和 Heap 组成. 其中
Heap(堆内存) = {Old + NEW = { Eden , from, to } }
NEW 也叫年轻代(Young Gen):
这里解释下,也有人说上图中的from,to是suvivor space a,survivor b
而且这两个区会交换的:
NEW:年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(命名为A和B)
• 当对象在堆创建时,将进入年轻代的Eden Space。
• 垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制 Old Gen
• 扫描A Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个Old对象,则将其移到Old Gen。
• 扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和BSuvivor Space。
我们可以看到:Young Gen垃圾回收时,采用将存活对象复制到到空的Suvivor Space的方式来确保不存在内存碎片,采用空间换时间的方式来加速内存垃圾回收。
JConsole使用
JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。JConsole从java5开始,在JDK中提供。用于对JVM中内存,线程和类等的监控。
其使用很简单,JDK在你的环境变量之后,就可以在命令行中输入:
jconsole
如果弹出窗口,说明配置可用。
弹出窗口如下:
上图中的Eden Space就是JAVA对象初始进入的地方,
Survivor Space是A Suvivor Space空间
Tenured Gen 是Old Gen也就是大家说的养老区,
垃圾回收描述:
在New Generation块中,垃圾回收一般用Copying的算法,速度快。这里面的截图表示新生代已经执行了300多次的垃圾回收。
每次GC的时候,存活下来的对象首先由Eden拷贝到某个Survivor Space, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到Old Generation中去。因此,每次GC后,Eden内存块会被清空。在Old Generation块中,垃圾回收一般用mark-compact的算法,速度慢些,但减少内存要求.
还是上图
说明采用mark-compact算法的养老区还没有执行GC,(0项收集);
回到这个例子,在堆内存的JCONSOLE面板中,有三个子项,分别是:
(一)Eden Space
其截图如下:
这个是新生代内存,从图上来看,整体的运行内存还是比较稳定的。
(二)A Suvivor Space
这个也是相当的稳定
(三)OLD
OLD养老区的内存一直呈现上升趋势,而且在这5个多小时里面,一直没有下降的趋势。这个是为什么呢?大家知道, 内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。那么这个现象会不会出现内存溢出呢?我们在前面了解到,OLD养老区的内存回收mark-compact,一般是比较慢的,所以这5个小时,没有发生过一次养老区的垃圾回收。当OLD去的空间不够时,JVM会在OLD区进行完全的垃圾收集(0级),这样就会避免出现OLD区的OOM错误。
因为堆内存是上述三张图的内存集合,那么下张图中,堆内存的线性增长也就有合理的解释了,由于OLD还没有进行垃圾回收,里面的对象越来越多,占用内存越来越大,也就影响了整体堆内存
JCONSOLE还能看到什么呢?
第三个TAB页签,能看到当前的线程数
第四个TAB页签,能够看到当前的JVM信息
最有一个线程数能够看到当前JMX暴露出现的JVM运行的整体信息,比如说GC的次数,GC的类型,时间等等;
后续
这个模拟程序会随着时间的推迟而OLD去内存线性增长,也就意味着堆区内存线性增长,难道一直等着OLD区来做mark-compact的算法的GC?
当然也是有方法去改进GC的算法的,先来了解一下基本的知识:
垃圾回收策略
评估垃圾回收策略的两个重要度量是:
• 吞吐量(Throughput ):JVM花费在垃圾回收上的时间越长,则吞吐量越低
• 暂停时间(Pause time):JVM垃圾回收过程当中有一个暂停期,在暂停期间,应用程序不能运行,暂停时间是暂停期的长度
非常遗憾的是,一般这两个指标是相互冲突的,改善其中一个会影响到另外一个,根据情景的不同我们决定是优先考虑吞吐量还是暂停时间,对于需要实时响应的应用,我们需要优先考虑暂停时间,对于后台运行应用,我们需要优先考虑吞吐量。
在考察各种垃圾回收器之前,我们需要了解一下几个重要的策略
• 并行(Parallel):并行表示使用多个线程同时进行垃圾回收的工作,此策略一般会从同时改善暂停时间和吞吐量,在有多CPU内核的服务器上,这是基本上我们要使用的策略。
• 并发(Concurrent):并行表示垃圾回收器的一些工作(譬如垃圾标记)与应用程序同时进行,这将更进一步缩短暂停时间,需要注意的是,同时垃圾回收器的复杂性会大大增大,基本上是会降低吞吐量,
• 内存碎片处理:有不压缩、压缩和拷贝三种策略,从空间上讲,拷贝将花费更多的内存(譬如如上内存管理的Young Gen,需要维持一个额外的Suvivor空间),从时间上来讲,不压缩会减低创建对象时的内存分配效率,在垃圾回收上,拷贝策略会比压缩策略更高效。
Sun JVM有4垃圾回收器:
• Serial Collector:序列垃圾回收器,垃圾回收器对Young Gen和Tenured Gen都是使用单线的垃圾回收方式,对Young Gen,会使用拷贝策略避免内存碎片,对Old Gen,会使用压缩策略避免内存碎片。基本上,在对内核的服务器上应该避免使用这种方式。在JVM启动参数中使用-XX:+UseSerialGC启用Serial Collector。
• Parallel Collector:并发垃圾回收器,垃圾回收器对Young Gen和Tenured Gen都是使用多线程并行垃圾回收的方式,对Young Gen,会使用拷贝策略避免内存碎片,对Old Gen,会使用压缩策略避免内存碎片。
在JVM启动参数中使用-XX:+UseParallelGC启用Parallel Collector。
• Parallel Compacting Collector:并行压缩垃圾回收器,与Parallel Collector垃圾回收类似,但对Tenured Gen会使用一种更有效的垃圾回收策略,此垃圾回收器在暂停时间上会更短。
在JVM启动参数中使用-XX:+UseParallelOldGC启用Parallel Compacting Collector。
• Concurrent Mark-Sweep (CMS) Collector:并发标志清除垃圾回收器,对Young Gen会使用与Parallel Collector同样的垃圾回收策略,对Tenured Gen,垃圾回收的垃圾标志线程与应用线程同时进行,而垃圾清除则需要暂停应用线程,但暂停时间会大大缩减,需要注意的是,由于垃圾回收过程更加复杂,会降低总体的吞吐量。
在JVM启动参数中使用:-XX:+UseConcMarkSweepGC启动
具体的CMS方式,有个哥们解释的更加清楚:
http://softbeta.iteye.com/blog/1315103
案例
首先解释下场景,服务端是一个通信服务器,接受客户端发过来的通信信息,并做业务处理;服务端采用JAVA中的MINA2框架,客户端可以任意,C++也好,JAVA也好,只要符合服务端规定的消息结构,发给通信服务器都能处理。
为了让大家更清楚,可以用MINA2框架中的时间服务器的例子来稍作修改模拟这个场景。系统环境,APACHE MINA2.0.4 + JDK1.6 + Eclipse3.7。
服务端代码如下:
public class MinaTimeServer { /** We will use a port above 1024 to be able to launch the server with a standard user */ private static final int PORT = 9123; /** * The server implementation. It's based on TCP, and uses a logging filter * plus a text line decoder. */ public static void main(String[] args) throws IOException { // Create the acceptor IoAcceptor acceptor = new NioSocketAcceptor(); // Add two filters : a logger and a codec acceptor.getFilterChain().addLast( "logger", new LoggingFilter() ); acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" )))); //acceptor.getFilterChain().addLast("exector", new ExecutorFilter(Executors.newCachedThreadPool())); acceptor.getFilterChain().addLast("exector", new ExecutorFilter()); // Attach the business logic to the server acceptor.setHandler( new TimeServerHandler() ); // Configurate the buffer size and the iddle time acceptor.getSessionConfig().setReadBufferSize( 2048 ); acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 ); // And bind ! acceptor.bind( new InetSocketAddress(PORT) ); } }
业务处理器(这里当作一个时间服务器,接受到信息之后就立刻返回服务器当前时间)
public class TimeServerHandler extends IoHandlerAdapter { /** * Trap exceptions. */ @Override public void exceptionCaught( IoSession session, Throwable cause ) throws Exception { cause.printStackTrace(); } /** * If the message is 'quit', we exit by closing the session. Otherwise, * we return the current date. */ @Override public void messageReceived( IoSession session, Object message ) throws Exception { String str = message.toString(); if( str.trim().equalsIgnoreCase("quit") ) { // "Quit" ? let's get out ... session.close(true); return; } // Send the current date back to the client Date date = new Date(); session.write( date.getTime()); System.out.println("Message written..."); } /** * On idle, we just write a message on the console */ @Override public void sessionIdle( IoSession session, IdleStatus status ) throws Exception { System.out.println( "IDLE " + session.getIdleCount( status )); } }
客户端JAVA代码:
import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; public class MinaTimeClient { public void mutiSendMsg() { // 创建客户端连接器. NioSocketConnector connector = new NioSocketConnector(); connector.getFilterChain().addLast("logger", new LoggingFilter()); connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset .forName("UTF-8")))); // 设置编码过滤器 connector.setConnectTimeoutMillis(30000); connector.setHandler(new TimeClientHandler());// 设置事件处理器 ConnectFuture cf = connector.connect(new InetSocketAddress("127.0.0.1", 9908));// 建立连接 cf.awaitUninterruptibly();// 等待连接创建完成 cf.getSession().write("hello");// 发送消息 cf.getSession().write("quit");// 发送消息 cf.getSession().getCloseFuture().awaitUninterruptibly();// 等待连接断开 connector.dispose(); } public static void main(String[] args) { /* MinaTimeClient client = new MinaTimeClient(); for(int i = 0; i < 1000; i ++) { client.mutiSendMsg(); }*/ //int size = keywordMap.size(); long startTime = System.currentTimeMillis(); int thread_num = 100; int client_num = 2000; // TODO Auto-generated method stub ExecutorService exec = Executors.newCachedThreadPool(); // 50个线程可以同时访问 final Semaphore semp = new Semaphore(thread_num); final CountDownLatch countDownLatch = new CountDownLatch(client_num); List<Thread> list = new ArrayList<Thread>(); // 模拟2000个客户端访问 for (int index = 0; index < client_num; index++) { final int NO = index; Thread run = new Thread() { public void run() { while(true){ try { // 获取许可 //semp.acquire(); new MinaTimeClient().mutiSendMsg(); // System.out.println(result); // Thread.sleep((long) (Math.random()) * 1000); // 释放 System.out.println("第:" + NO + " 个"); // semp.release(); Thread.sleep(500); //countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } } }; list.add(run); //exec.execute(run); } for (Thread thread : list) { thread.start(); } //try { //countDownLatch.await(); System.out.println("end time::::" + (System.currentTimeMillis() - startTime)); // 退出线程池 // exec.shutdown(); //} catch (InterruptedException e) { // TODO Auto-generated catch block // e.printStackTrace(); //} } }
客户端处理类:
public class TimeClientHandler extends IoHandlerAdapter { public TimeClientHandler() { } @Override public void messageReceived(IoSession session, Object message) throws Exception { System.out.println(message);// 显示接收到的消息 } }
代码准备好之后,启动通信服务器服务端,然后启动客户端;
然后在打开你的jconsole界面,选择需要观察的那个服务端的JAVA进程;
首页就是类似这样的一个界面(注意:这篇文章里面所有的截图都是在模拟程序运行5个小时以后截下来的):
JAVA内存模型简介
我们知道:JAVA的内存模型是这样的,简单的来说,可以分为堆和非堆内存,下面的著名的这个图大家经常会看到:
内存由 Perm 和 Heap 组成. 其中
Heap(堆内存) = {Old + NEW = { Eden , from, to } }
NEW 也叫年轻代(Young Gen):
这里解释下,也有人说上图中的from,to是suvivor space a,survivor b
而且这两个区会交换的:
NEW:年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(命名为A和B)
• 当对象在堆创建时,将进入年轻代的Eden Space。
• 垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制 Old Gen
• 扫描A Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个Old对象,则将其移到Old Gen。
• 扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和BSuvivor Space。
我们可以看到:Young Gen垃圾回收时,采用将存活对象复制到到空的Suvivor Space的方式来确保不存在内存碎片,采用空间换时间的方式来加速内存垃圾回收。
JConsole使用
JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。JConsole从java5开始,在JDK中提供。用于对JVM中内存,线程和类等的监控。
其使用很简单,JDK在你的环境变量之后,就可以在命令行中输入:
jconsole
如果弹出窗口,说明配置可用。
弹出窗口如下:
上图中的Eden Space就是JAVA对象初始进入的地方,
Survivor Space是A Suvivor Space空间
Tenured Gen 是Old Gen也就是大家说的养老区,
垃圾回收描述:
在New Generation块中,垃圾回收一般用Copying的算法,速度快。这里面的截图表示新生代已经执行了300多次的垃圾回收。
每次GC的时候,存活下来的对象首先由Eden拷贝到某个Survivor Space, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到Old Generation中去。因此,每次GC后,Eden内存块会被清空。在Old Generation块中,垃圾回收一般用mark-compact的算法,速度慢些,但减少内存要求.
还是上图
说明采用mark-compact算法的养老区还没有执行GC,(0项收集);
回到这个例子,在堆内存的JCONSOLE面板中,有三个子项,分别是:
(一)Eden Space
其截图如下:
这个是新生代内存,从图上来看,整体的运行内存还是比较稳定的。
(二)A Suvivor Space
这个也是相当的稳定
(三)OLD
OLD养老区的内存一直呈现上升趋势,而且在这5个多小时里面,一直没有下降的趋势。这个是为什么呢?大家知道, 内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。那么这个现象会不会出现内存溢出呢?我们在前面了解到,OLD养老区的内存回收mark-compact,一般是比较慢的,所以这5个小时,没有发生过一次养老区的垃圾回收。当OLD去的空间不够时,JVM会在OLD区进行完全的垃圾收集(0级),这样就会避免出现OLD区的OOM错误。
因为堆内存是上述三张图的内存集合,那么下张图中,堆内存的线性增长也就有合理的解释了,由于OLD还没有进行垃圾回收,里面的对象越来越多,占用内存越来越大,也就影响了整体堆内存
JCONSOLE还能看到什么呢?
第三个TAB页签,能看到当前的线程数
第四个TAB页签,能够看到当前的JVM信息
最有一个线程数能够看到当前JMX暴露出现的JVM运行的整体信息,比如说GC的次数,GC的类型,时间等等;
后续
这个模拟程序会随着时间的推迟而OLD去内存线性增长,也就意味着堆区内存线性增长,难道一直等着OLD区来做mark-compact的算法的GC?
当然也是有方法去改进GC的算法的,先来了解一下基本的知识:
垃圾回收策略
评估垃圾回收策略的两个重要度量是:
• 吞吐量(Throughput ):JVM花费在垃圾回收上的时间越长,则吞吐量越低
• 暂停时间(Pause time):JVM垃圾回收过程当中有一个暂停期,在暂停期间,应用程序不能运行,暂停时间是暂停期的长度
非常遗憾的是,一般这两个指标是相互冲突的,改善其中一个会影响到另外一个,根据情景的不同我们决定是优先考虑吞吐量还是暂停时间,对于需要实时响应的应用,我们需要优先考虑暂停时间,对于后台运行应用,我们需要优先考虑吞吐量。
在考察各种垃圾回收器之前,我们需要了解一下几个重要的策略
• 并行(Parallel):并行表示使用多个线程同时进行垃圾回收的工作,此策略一般会从同时改善暂停时间和吞吐量,在有多CPU内核的服务器上,这是基本上我们要使用的策略。
• 并发(Concurrent):并行表示垃圾回收器的一些工作(譬如垃圾标记)与应用程序同时进行,这将更进一步缩短暂停时间,需要注意的是,同时垃圾回收器的复杂性会大大增大,基本上是会降低吞吐量,
• 内存碎片处理:有不压缩、压缩和拷贝三种策略,从空间上讲,拷贝将花费更多的内存(譬如如上内存管理的Young Gen,需要维持一个额外的Suvivor空间),从时间上来讲,不压缩会减低创建对象时的内存分配效率,在垃圾回收上,拷贝策略会比压缩策略更高效。
Sun JVM有4垃圾回收器:
• Serial Collector:序列垃圾回收器,垃圾回收器对Young Gen和Tenured Gen都是使用单线的垃圾回收方式,对Young Gen,会使用拷贝策略避免内存碎片,对Old Gen,会使用压缩策略避免内存碎片。基本上,在对内核的服务器上应该避免使用这种方式。在JVM启动参数中使用-XX:+UseSerialGC启用Serial Collector。
• Parallel Collector:并发垃圾回收器,垃圾回收器对Young Gen和Tenured Gen都是使用多线程并行垃圾回收的方式,对Young Gen,会使用拷贝策略避免内存碎片,对Old Gen,会使用压缩策略避免内存碎片。
在JVM启动参数中使用-XX:+UseParallelGC启用Parallel Collector。
• Parallel Compacting Collector:并行压缩垃圾回收器,与Parallel Collector垃圾回收类似,但对Tenured Gen会使用一种更有效的垃圾回收策略,此垃圾回收器在暂停时间上会更短。
在JVM启动参数中使用-XX:+UseParallelOldGC启用Parallel Compacting Collector。
• Concurrent Mark-Sweep (CMS) Collector:并发标志清除垃圾回收器,对Young Gen会使用与Parallel Collector同样的垃圾回收策略,对Tenured Gen,垃圾回收的垃圾标志线程与应用线程同时进行,而垃圾清除则需要暂停应用线程,但暂停时间会大大缩减,需要注意的是,由于垃圾回收过程更加复杂,会降低总体的吞吐量。
在JVM启动参数中使用:-XX:+UseConcMarkSweepGC启动
具体的CMS方式,有个哥们解释的更加清楚:
http://softbeta.iteye.com/blog/1315103
发表评论
-
大型跨境电商JVM调优经历
2018-02-23 14:01 10212前提:某大型跨境电商业务发展非常快,线上机器扩容也很频繁, ... -
Spring与策略模式
2014-02-09 18:15 43448... -
MINA2的多线程模型问题探讨
2012-03-13 22:45 14663最近和一友人沟通,他在一个项目中使用MINA2,他反馈在启 ... -
JAVA应用Crash错误分析
2010-12-30 17:59 6280昨天晚上启动jboss之后,发现点击某个页面,总是cras ... -
JVM配置参数中文说明
2010-12-14 22:38 15211、-Xmixed mixed mode ... -
推荐一个Eclipse的UML插件:AmaterasUML
2010-11-19 15:21 11136介绍介绍Eclipse的UML插件:AmaterasUML ... -
Java的比较
2010-11-18 22:13 1583一、== 适用于基本对象值的比较,其他对象是比较引用 ... -
classloader相关基础知识
2010-11-18 22:10 1223JVM jvm是jre里头一个动态 ... -
MINA2的实用参考手册
2010-10-28 08:03 1589MINA2整理的入门资料,是最近项目中使用的一个总结。分享给有 ... -
JAVA并发之Exchanger
2010-10-20 14:40 6337JDK1.5中有一个Exchanger类,可以用来完成线程 ... -
MINA2的一个BUG
2010-10-20 09:22 10189我们知道,在MINA2中,发送和接受时两个独立的工作线程 ... -
一道JAVA面试题
2010-10-18 22:01 2137题目:一列整型数组,从第一个开始,累加后续两个数字,得到 ... -
MINA2收包中对粘包的处理
2010-10-14 08:22 11289MINA2中(MINA2 RC版本,MINA2.0正式版已 ... -
MINA2处理转发的一种解决方式
2010-09-19 17:16 3718场景:服务端开了两个SOCKET服务,分别对应两种客户端, ... -
MINA2直接采用log4j做日志记录
2010-08-12 22:03 4589使用MINA2的时候,每次都要多两个slf4j的包,但是我 ... -
ECLIPSE java.lang.OutOfMemoryError: PermGen space 错误
2010-08-11 09:54 40286ECLIPSE 最近一段时间经常报错,看了日志,有如下错误信息 ... -
MINA2判断报文边界
2010-08-05 22:38 7228我们知道,进行SOCKET tcp/ip通信的时候,不知道每次 ... -
MINA2中的拆包组包的处理及一些方法
2010-08-03 22:46 116371.position 例: position() ... -
JAVA内存模型
2010-07-07 20:36 2278一、Java把内存划分成 ... -
JAVA动态代理解析
2010-06-11 09:25 1366众所周知,JDK的动态代理模式必须实现接口。 以下面的源码为 ...
相关推荐
4. **性能调优**:通过监控工具(如JConsole、VisualVM等)分析JVM的内存、CPU使用情况,调整JVM参数以提升应用性能。 5. **类加载机制**:理解双亲委派模型,以及如何自定义类加载器以满足特定需求。 **NIO解析**...
5. **堆**:这是JVM中最大的一块内存区域,所有对象实例和数组都在这里分配内存。堆是线程共享的,Java的自动垃圾回收机制主要在这里工作,负责管理对象的生命周期。 6. **直接内存**:虽然不属于JVM规范中的标准...
JVM的基本配置参数如`-Xmx`、`-Xms`、`-Xss`、`-Xmn`、`-XX:MetaspaceSize`等用于调整内存分配,以满足应用的需求。例如,`-XX:+PrintGCDetails`参数可以开启详细垃圾回收日志,帮助调试和优化垃圾回收性能。 在...
2. **JVM调优**:调优主要包括堆内存分配、垃圾收集策略选择、类加载优化、线程池调整等。例如,通过-Xms和-Xmx设置堆大小,-XX:NewRatio调整新生代和老年代比例,-XX:+UseConcMarkSweepGC启用特定的垃圾收集器。...
- **直接内存(Direct Memory)**:不在JVM内存区内,通过`java.nio.ByteBuffer.allocateDirect()`方式分配内存。 ##### 2. HotSpot运行时数据区域关系图 HotSpot虚拟机是Sun Microsystems开发的一款高性能的JVM实现...
### JVM基础知识及性能调优 #### 一、JVM架构概览 Java虚拟机(JVM)作为Java程序运行的...通过深入理解JVM的架构、内存管理和垃圾回收机制,结合适当的性能调优策略和工具,可以显著提升Java应用的运行效率和稳定性。
- 类加载机制:JVM通过类加载器将.class文件加载到内存中,分为启动类加载器、扩展类加载器和应用类加载器。 - 字节码执行:JVM解析字节码,通过解释器或即时编译器(JIT)将其转换为机器码执行。 - 类加载过程:...
- **直接内存**:非JVM管理的内存,但对性能有提升,如NIO库直接分配堆外内存。 2. **JVM内存参数** - **Xms和Xmx**:分别设置初始堆大小和最大堆大小,确保系统不会因频繁调整内存大小而影响性能。 - **NewSize...
直接内存是Java堆外的内存区域,如通过NIO直接分配的内存;本地方法栈负责管理Java中的本地方法;垃圾回收系统负责回收堆内存中不再使用的对象;PC寄存器存储线程当前执行的指令地址;执行引擎负责执行类加载后的...
- 虚拟机启动时创建的主要内存区域,几乎所有对象实例都在这里分配内存。 - 堆是所有线程共享的区域,可以被细分为新生代和老年代,以及更细的分区如Eden区、S0区和S1区等。 **2.4 直接内存** - 不属于Java堆的一...
- **Java堆**:所有对象实例和数组都在这里分配内存,是JVM最大的一块内存区域,也是GC的主要区域。 - **方法区**(在Java 8中被元空间替换):存储已加载的类信息、常量、静态变量等,也称为永久代。 - **运行时...
5. **内存分配与回收策略**: - **对象优先在Eden分配**。 - **大对象直接进入老年代**。 - **长期存活的对象进入老年代**。 - **动态对象年龄判定**:根据Survivor区空间和对象年龄来决定晋升老年代的条件。 -...
- **监控工具**:使用JConsole、VisualVM、JProfiler等工具进行实时监控,分析内存分配、GC行为及性能瓶颈。 2. **Tomcat调优**: - **线程池配置**:调整`maxThreads`, `minSpareThreads`, `acceptCount`等参数...
**JVM调优**是Java开发中的重要环节,它涉及到程序性能优化,旨在提升应用程序的运行效率,减少内存消耗,缩短响应时间,以及提高系统的稳定性和可扩展性。JVM(Java虚拟机)是Java程序运行的基础,通过理解JVM的...
Java筑基面试专题系列是针对Java开发者进阶的深度学习资料,主要涵盖了并发编程、Netty框架以及JVM这三个核心领域。在这个专题中,我们将会深入探讨这些关键...对于JVM,可以结合真实场景进行调优实践,以加深理解。
- 直接内存不属于 JVM 运行时数据区,但它可以通过 NIO 直接分配,减少数据复制,提高性能。 - 直接内存的大小受本机物理内存限制,过度使用可能导致 `OutOfMemoryError`。 4. **JVM 参数调优**: - `-Xms` 和 `...
非堆内存包括直接内存和Java NIO缓冲区,这些不归属JVM堆管理,但同样需要优化。 新生代和老年代的大小调整应基于应用程序的特性。如果多数对象生命周期短,增大Eden区可以减少Minor GC的频率;反之,如果对象生命...
因此,当使用分层编译和大型应用时,可能需要调整内存分配策略以确保足够的空间。 在64位系统上,由于地址空间较大,可以分配更多的内存给JVM,但仍然需要考虑操作系统和其他进程对内存的需求。为了监控和调整这些...
#### 内存分配与回收策略 在JVM中,对象的创建通常发生在年轻代的Eden区。当Eden区满时,会触发一次Minor GC,将存活的对象转移到Survivor区或者直接晋升到老年代。对于长期存活的对象,则会被晋升到老年代。 ####...
5. **IO与NIO**:讲解Java的输入输出系统,包括传统的IO流和非阻塞的NIO模型,以及文件操作、网络通信等相关内容。 6. **反射与动态代理**:Java反射机制允许在运行时检查和修改程序的行为,动态代理则用于实现AOP...