jvm优化及resin配置优化
- 格式:ppt
- 大小:1.50 MB
- 文档页数:22
JVM性能调优Posted on 2010-06-26 06:48 chen77716阅读(1208) 评论(3) 编辑收藏最近因项目存在内存泄漏,故进行大规模的JVM性能调优,现把经验做一记录。
一、JVM内存模型及垃圾收集算法1.根据Java虚拟机规范,JVM将内存划分为:∙New(年轻代)∙Tenured(年老代)∙永久代(Perm)其中New和Tenured属于堆内存,堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,Perm 不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize等参数调整其大小。
∙年轻代(New):年轻代用来存放JVM刚分配的Java对象∙年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代∙永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设臵为128M就足够,设臵原则是预留30%的空间。
New又分为几个部分:∙Eden:Eden用来存放JVM刚分配的对象∙Survivor1∙Survivro2:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到Tenured。
显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。
2.垃圾回收算法垃圾回收算法可以分为三类,都基于标记-清除(复制)算法:∙Serial算法(单线程)∙并行算法∙并发算法JVM会根据机器的硬件配臵对每个内存代选择适合的回收算法,比如,如果机器多于1个核,会对年轻代选择并行算法,关于选择细节请参考JVM调优文档。
稍微解释下的是,并行算法是用多线程进行垃圾回收,回收期间会暂停程序的执行,而并发算法,也是多线程回收,但期间不停止应用执行。
JVM优化配置OOM这个缩写就是Java程序开发过程中让人最头痛的问题:Out ofMemory。
在很多开发人员的开发过程中,或多或少的都会遇到这类问题,这类问题定位比较困难,往往需要根据经验来判断可能出现问题的代码。
原因主要是两个:对象没有被释放(多种情况引起,往往是比较隐蔽的引用导致被Hold而无法被回收)。
另一种就是真的Memory不够用了,需要增加JVM的Heap来满足应用程序的需求。
最近有同事发的关于解决OOM的问题,让我了解了原来OOM 除了在JVM Heap不够时会发生,在NativeHeap不够的时候也会发生,同时JVM Heap和NativeHeap存在着相互影响和平衡的关系,因此就仔细的去看了关于OOM和JVM配置优化的内容。
OOM在其他语言类似于C,Delphi等等由于内存都是由自己分配和管理,因此内存泄露的问题比较常见,同时也是很头痛的一件事情。
而Java的对象生命周期管理都是JVM来做的,简化了开发人员的非业务逻辑的处理,但是这种自动管理回收机制也是基于一些规则的,而违背了这些规则的时候,就会造成所谓的“Memory Leak”。
OOM(Java Heap)错误提示:ng.OutOfMemoryError。
这类OOM是由于JVM分配的给应用的HeapMemory已经被耗尽,可能是因为应用在高负荷的情况下的却需要很大的内存,因此可以通过修改JVM参数来增加Java HeapMemory(不过也不能无限制增加,后面那种OOM有可能就是因为这个原因而产生)。
另一种情况是因为应用程序使用对象或者资源没有释放,导致内存消耗持续增加,最后出现OOM,这类问题引起的原因往往是应用已不需要的对象还被其他有效对象所引用,那么就无法释放,可能是业务代码逻辑造成的(异常处理不够例如IO等资源),也可能是对于第三方开源项目中资源释放了解不够导致使用以后资源没有释放(例如JDBC的ResultSet等)。
《Java性能调优指南》随着互联网的飞速发展,Java作为一种重要的编程语言,被越来越广泛地应用于各个领域。
但是,Java程序的性能问题也随之出现。
如何调优Java 程序的性能,成为了每个开发人员需要解决的难题。
本文将为大家介绍Java性能调优的指南。
一、JVM参数设置JVM(Java虚拟机)参数设置是Java性能调优的关键。
JVM有众多的参数,不同的参数设置会对Java程序的性能产生不同的影响。
常用的JVM参数设置包括以下几个方面:1. 内存设置内存是Java程序的一大瓶颈。
如果内存设置不合理,会导致Java程序频繁地进行垃圾回收,造成程序的延迟和不稳定。
在设置内存参数时需要注意以下几点:- -Xmx: 最大堆内存,设置合理的最大堆内存大小可以减少JVM的垃圾回收次数,提高程序性能。
- -Xms: 初始堆内存,设置合理的初始堆内存大小可以加快程序启动时间,提高程序性能。
- -XX:NewRatio: 新生代与老年代的比例,如果设置得当,可以减少垃圾回收的次数。
通常新生代的大小为总堆容量的1\/3或1\/4,老年代的大小为总堆容量的2\/3或3\/4。
2. 垃圾回收设置垃圾回收是Java程序中必不可少的一部分。
合理的垃圾回收参数设置可以提高程序性能。
常用的垃圾回收参数设置包括以下几点:- -XX:+UseParallelGC: 使用并行GC,适用于多核CPU。
- -XX:+UseConcMarkSweepGC: 使用CMS GC,适用于大型Web应用程序。
- -XX:+UseG1GC: 使用G1 GC,适用于大内存应用程序。
3. JIT设置JIT(即时编译器)是Java程序中非常重要的一部分。
合理的JIT参数设置可以提高程序的性能。
常用的JIT参数设置包括以下几点:- -XX:+TieredCompilation: 启用分层编译,可以提高程序启动时间和性能。
- -XX:CompileThreshold: JIT编译阈值,设置JIT编译的最小方法调用次数,可以提高程序性能。
Java虚拟机JVM各调优参数说明Java虚拟机(JVM)是Java程序运行的环境,它负责将Java源代码编译为字节码,并在运行时执行这些字节码。
JVM的性能对于Java应用程序的性能至关重要。
为了优化JVM的性能,我们可以通过调整一些参数来改变其行为。
下面是JVM各调优参数的详细说明。
1. -Xms和-Xmx:这两个参数用于设置JVM的初始堆大小和最大堆大小。
初始堆大小表示JVM在启动时申请的内存大小,最大堆大小表示JVM所能申请的最大内存大小。
可以使用以下命令设置初始堆大小为1GB,最大堆大小为2GB:-Xms1g -Xmx2g。
2. -XX:NewSize和-XX:MaxNewSize:这两个参数用于设置新生代的初始大小和最大大小。
新生代是JVM堆的一部分,用于存放新创建的对象。
可以使用以下命令设置新生代的初始大小为256MB,最大大小为512MB:-XX:NewSize=256m -XX:MaxNewSize=512m。
3. -XX:SurvivorRatio:这个参数用于设置新生代中Eden区和Survivor区的比例。
Eden区是新对象的分配区域,Survivor区是用于存放幸存的对象的区域。
可以使用以下命令设置Eden区和Survivor区的比例为8:1:-XX:SurvivorRatio=84. -XX:MaxTenuringThreshold:这个参数用于设置对象在Survivor区中的最大年龄。
当对象在Survivor区中存活的时间超过这个阈值时,它将被晋升到老年代。
可以使用以下命令设置最大年龄为15:-XX:MaxTenuringThreshold=155. -XX:PermSize和-XX:MaxPermSize:这两个参数用于设置永久代的初始大小和最大大小。
永久代用于存放类的元数据、静态变量和常量池等信息。
可以使用以下命令设置永久代的初始大小为128MB,最大大小为256MB:-XX:PermSize=128m -XX:MaxPermSize=256m。
Resin Session AnalysisWritten by Wooce 1. Resin的Reliability和Load balance机制Resin在Web Server中的内嵌模块处理请求的顺序:1)Web Server接收到请求2)内嵌模块(mod_caucho, mod_isapi等)检查是否由Resin处理的请求3)内嵌模块选中一个后台JVM,也就是一个<srun>:a.如果是已有的session, 则选中拥有该session的那一个JVMb.如果是新的请求,则采用轮循策略交由下一个<srun>处理4)内嵌模块通过TCP Socket把请求发给选中的后台JVM5)内嵌模块再通过同一TCP Socket连接接收后台JVM的响应结果.所有的有关内嵌模块和后台JVM均在resin.conf这一配置文件中配置, 使维护变得容易.因为内嵌模块需要决定选用哪一个JVM,所以负载平衡由内嵌模块控制.从JVM的角度看,来自内嵌模块的请求和HTTP请求是同样的,除了编码稍有不同.例如, Resin1.2或以上,同一个JVM可以在同一个端口8080同时作为srun和httpd服务器服务.轮循策略虽然简单, 但它实际上和其他复杂的负载平衡测率一样有效, 并且正由于简单, 它更加健壮和快速.1.1单机最廉价的备份策略. 它在单台机器上运行一个Web Server和两个JVM. 其中一个是主服务JVM, 另一个是后备JVM, 如果主JVM失效, 将启用后备JVM维持Servlet Engine仍然可用. 在resin.conf中的相应配置例子:启动时应该分别单独地启动这两个sun进程. 例如unix下:在NT下:1.2单Web Server, 多JVM当有多台计算机的时候, 可以把负载分担到一台Web Server和多个JVM上, 这比用路由器实现负载平衡较为廉价. 并且采用Resin自己的负载平衡机制,可以保证使同一个session 停留在同一台计算机上.配置举例:各台计算机可以共享使用同一个resin.conf, 但也可以为Web Server和每个JVM分别使用不同的resin.conf.为了确保session停留在固定的机器上, Resin对cookie的编码中加上了主机号标识. 1.3多Web Server和JVM对于大型的服务网站, 一般使用多台计算机, 并且每台计算机都分别运行一个Web Server和一个JVM, 由Router负责把负载分担到不同的机器上.对于这种方法, 由于Router随机地把负载分配到某台机器上, 所以发给某台host的请求的Session不一定是这台Host拥有的.因此需要把任何持久的session维护在一台中央服务器如JDBC数据库上或者采用其他方法使能够由session的id找到拥有该session的机器.即使对于这种Router负载平衡方法, 仍然可以用Resin自己的load balance策略提高可靠性. 对于每一台host上的web server, 都应该首先使用同一台host上运行的JVM进行处理, 但同时可以另一台Host上运行的JVM作为后备.上面例子中host1, host2,host3上的JVM只有当localhost上的JVM失败时才会被启用.另一个例子:1.4多Web Server, 单JVM多个Web Server可以同时使用同一个JVM处理请求. 例如, 我们有一个高速的一般的Web Server和一个采用SSL加密的Web Server的时候, 这两个Web Server可以由同一个JVM 处理请求(虽然如果有一个后备JVM的话会更好), 这简化了引入SSL的开发.2. Resin内部实现distributed sessions的机制2.1 session的backing store的概念和内存中的session的同步更新在Resin设置成load balance over multiple machine的情况, 各台运行web server和JVM 的机器组成一个load balancing pool.为实现load balance over multiple machine下的distributedsession, 必须考虑一个session在被生成后, 在load balancing pool中其他不拥有该session的机器如何访问它, 以及如何同步它们. 在Resin中是通过把session存储到backing store中来解决的, 一个JVM创建了一个新的session后,就把它存储到backing store去, 然后只要保证以下条件:(1)每个内存中有此session的JVM如果更新了此session, 就把更新后的session写到backing store中去, 并且backing store里做到在写某一session的时候不会同时接受读取同一session的请求.(2)每个JVM在读取session的时候,如果它的内存cache中还没有该session, 一定到backing store去读取.(3)某个JVM: Server A在读取session的时候,如果它的内存cache中已有该session, 这时只要满足以下的其中一种情况即可:a. 在(1)中写session到backing store里时保证同时更新了Server A的内存cache中的sessionb. 到backing store中再load出session并以此更新内存cache中的session显然, 满足了以上3个条件, 不同的机器存取同一session的时候就有很好的同步性, 并且亦有较好的性能.在Resin中, backing store可采用3种实现方式: (参看src/com/caucho/server/http/下的SessionStore.java,SessionFileStore.java,SessionJdbcStore.java和SessionRingStore.java)(1)SessionFileStore(这一方式在我所看的Resin2.0.2的代码里实际上还未实现)session的backing store在生成它的Host的文件系统(Resin似乎考虑在将来实现一个在不同的运行JVM的机器之间共享文件的网络文件系统VFS)上.(2)SessionJdbcStore在一台中央服务器上的database作为backing store, 每台Host上生成的session都通过JDBC存储到中央服务器的database中去.(3)SessionRingStore在这种方式中, load balancing pool中所有的运行JVM的机器按host id从小到大(host id 的首字母从’a’到’z’的顺序)组成一个JVM的index号的环, 当环中的某一台Host生成了一个session(session里记录了生成它的JVM的index号)的时候, 就以生成该session的JVM的index号开始, 在环上顺序查找, 只要某一个index的JVM是可用的, 就把session存储到运行该JVM的机器的本地文件系统中, 这样在两台机器上都分别存储了这个session的副本的时候就停止. 明显可看出, 一般情况下(所有JVM都没有crash down)每个session都有两个副本分别存储在生成它的那台机器和环中的下一台机器的文件系统中.Resin采用上述3种方式中的哪种方式来实现distributed session决定于:IF resin.conf中session-config/file-store和其下的directory的tag不为空THEN 采用SessionFileStore;ELSE IF resin.conf中session-config/tcp-store为true THEN 采用SessionRingStore;ELSE IF resin.conf中session-config/jdbc-store不为空THEN 采用SessionJdbcStore;在Resin的实现中,(1)SessionRingStore方式一定能满足上面所说的3个条件, 同步性最好.而对于(2)SessionFileStore和SessionJdbcStore两种方式a. 当resin.conf中session-config/always-load-session为false的时候, 如果一个JVM读入了session在内存cache中, 以后除非timeout再从backing store中reload的情况, 以后有请求都一定从内存cache中读该session, 不能看到其他JVM在此期间对该session 的更新, 这时同步性最差, 但由于不需从backing store中读取, 响应速度最快;b. 当resin.conf中session-config/always-load-session为true的时候, 这时任何请求都会到backing store中读取, 同步性好, 但由于session的内存cache实际上不起作用, 对请求的响应速度最慢.2.2 相关类分析2.2.1 QSession类实现HttpSession接口, 在Resin系统中某个JVM内存中的session就是一个QSession对象, JVM内存cache中的QSession对象需要和backing store中的session保持同步更新.2.2.2 VirtualHost类在一个JVM上处理的所有Servlet application的集合.2.2.3 SrunConnection类维护本JVM到另一个JVM的一个用以交换session的socket连接.2.2.4ServletServer类实现Server接口其他:hostList: ArrayList类型(VirtualHost对象)addHost(String id, RegistryNode node, String appDir)函数:根据(id, node, appDir)新建一个VirtualHost对象并存入HashTable类型成员hosts中.init()函数:初始化invocationCache(LRUCache类型)等内存中的Cache;调用initHosts()函数初始化hosts 等;2.2.5 SessionStore类负责session的访问(读取/存储)操作的基类, SessionFileStore,SessionJdbcStore, SessionRingStore等都是它的子类.2.2.5SessionBacking类封装session存储在file system上的backing path上时的访问操作的接口.2.2.6 SessionManager类管理一个servlet application的所有session重要数据成员和函数:2.2.6SessionRingStore类当以Ring方式的backing store存储distributed sessions时SessionStore类的实现.SessionRingStore.java的void store(QSession session)函数:int sessionIndex = session.getSrunIndex(); //生成该QSession对象的JVM的index号int srunIndex = manager.getSrunIndex(); //执行本函数的JVM的index号int srunCount = manager.getSrunCount(); //Load balancing pool中所有JVM的数目if (sessionIndex < 0)sessionIndex = srunIndex;int index = sessionIndex; //在环中从生成该QSession对象的JVM的index号开始ReadWritePair rws = null;boolean hasSelf = false;int saveCount = 2; //分别存储2个副本到2个JVM的file system中for (int i = srunCount - 1; i >= 0 && saveCount > 0; i--) {//在环上查找一周,并且成功存//储2个副本时就停止if (index == srunIndex) { //环上的当前index号是本机的index号, 令hasSelf为truehasSelf = true; //使在后面的代码段中将会调用StoreSelf()函数存储Session副saveCount--; //本到本机的file system中}else {rws = server.getRecycleConnection(index); //环上当前检查的index号是其他机器,if (rws != null) { //取出可用的socket连接把QSession对象传给index号host让它try { //存储或更新到它的本地file sytem(由RunnerRequest.java处理) saveSession(tempStream, backing.getId(), index, rws);saveCount--;} catch (IOException e) {rws.getReadStream().close();rws.getWriteStream().close();rws = null;}}if (rws == null) {rws = server.getSessionConnection(index);if (rws != null) {try {saveSession(tempStream, backing.getId(), index, rws);saveCount--;} catch (IOException e) {rws.getReadStream().close();rws.getWriteStream().close();rws = null;}}}}index = (index + 1) % srunCount; //取环中的下一个JVM}if (hasSelf) {ReadStream is = tempStream.openRead(true);storeSelf(session, is); //存储session到本地file systemis.close();storeSelf()函数如下:private void storeSelf(QSession session, ReadStream is){SessionBacking backing = session.getBacking();synchronized (backing) { //通过异步synchronized避免多个请求时的写冲突try {Path sessionPath = backing.getPath(); //获得session在本地file system上的存储路径WriteStream os = sessionPath.openWrite();os.writeStream(is);os.close();} catch (IOException e) {if (dbg.canWrite())dbg.log(e);}}boolean load(QSession session)throws Exception{SessionBacking backing = session.getBacking();if (backing == null)return false;int sessionIndex = session.getSrunIndex();int srunIndex = manager.getSrunIndex();int srunCount = manager.getSrunCount();if (sessionIndex < 0)sessionIndex = srunIndex;int updateCount = session.getUpdateCount();/* servers A and B don't need auto-reloading, but server C does.这里server A和server B即指存有session副本的那两个server,当本JVM是server A或server B之一的时候, 显然本JVM的内存cache里的session 肯定是整个load balancing pool中最新的, 所以从这里退出来后回到SessionManager类//的QSession getSession(String key, long now)函数往下执行到else if (sessionStore != null && now > 0 &&(reloadEachRequest || session.needsLoad()))load(session, now);这里session.needsLoad()函数必须返回false, 反之不是server A和server B的情况session.needsLoad()函数就必须返回true以便调用load(session,now)从server A和server B 上获取最新更新的session, 所以在这里的符合条件判断时的代码段内有session.setNeedsLoad(true);语句*/if (srunIndex != sessionIndex &&(sessionIndex + 1) % srunCount != srunIndex) {updateCount = -1;session.setUpdateCount(-1);session.setNeedsLoad(true);}ReadWritePair rws = null;int index = sessionIndex;int loadCount = 2;// Find a live serverfor (int i = srunCount - 1; i >= 0 && loadCount > 0; i--) {try {if (srunIndex != index) {rws = server.getRecycleConnection(index);if (rws != null) {try {if (loadSession(session, index, backing.getId(),rws, updateCount)) {loadCount--;updateCount = session.getUpdateCount();}} catch (IOException e) {rws.getReadStream().close();rws.getWriteStream().close();rws = null;}}if (rws == null) {rws = server.getSessionConnection(index);if (rws != null &&loadSession(session, index, backing.getId(),rws, updateCount)) {updateCount = session.getUpdateCount();loadCount--;}}}else if (loadSelf(session, updateCount)) {if (dbg.canWrite()) {dbg.log("[" + server.getServerId() + "] self-load(" + index + ") " +backing.getId() + " " + backing.getPath());}updateCount = session.getUpdateCount();loadCount--;}} catch (Exception e) {if (dbg.canWrite())dbg.log(e);}if (srunCount > 1)index = (index + 1) % srunCount;return loadCount < 2 && updateCount > 0;}2.3 对Session的处理流程2.3.1 新的请求(需要新生成一个原来在backing store中没有的session)的情况当有新的请求时,1) 执行Request.getSession(boolean create=true) (指调用Request类的getSession(Boolean create)函数, 并传参数create为true,下同) .2) Request.getSession(boolean create)调用AbstractRequest.createSession(boolean create, boolean hasOldSession)函数.3) AbstractRequest.createSession(boolean create, boolean hasOldSession)最后调用SessionManager.createSession(String oldId, long date, int sessionGroup,HttpServletRequest request)创建QSession对象(但只在内存中, 并未存储到backing store)4) 对Request的处理完成, 调用Request.finish().5)Request.finish()调用QSession.finish().6)QSession.Finish()调用SessionStore.Store(QSession Session)完成将session存储到backing store的过程.2.3.2对旧有Session的请求(backing store中存在此id的session)的情况1)执行Request.getSession(Boolean create=false)2)Request.getSession(boolean create)调用AbstractRequest.createSession(boolean create,boolean hasOldSession).3)AbstractRequest.createSession(boolean create, boolean hasOldSession)调用SessionManager.getSession(String key, long now)4)SessionManager.getSession(String key, long now)在内存Cache中查找此QSession对象, 如果需要reload, 还要从backing store中读取QSession对象的更新的值, 然后返回QSession对象.2.3.3非法请求(整个系统中不存在此id的session)的情况在Request类中的boolean isRequestedSessionIdValid()函数即能判断, 它将调用Request.getSession(false)在本JVM的内存cache和backing store中查找此id的session, 如果都找不到则返回false.附AbstractRequest.java的createSession(boolean create, boolean hasOldSession)函数的注解: protected QSession createSession(boolean create, boolean hasOldSession){Application application = getApplication();SessionManager manager = application.getSessionManager();String id = getRequestedSessionId(); //读取请求中发过来的session idlong now = Alarm.getCurrentTime();QSession session;if (id != null && id.length() > 6) {session = manager.getSession(id, now); //从内存cache中读取此id的QSession对象, 没if (session == null) { //有则创建一个}else if (session.isValid()) //成功则返回该QSession对象return session;elseid = null;}if (hasOldSession)id = null;if (! create)return null;// Must accept old ids because different applications in the same// server must share the same cookie//// But, if the session group doesn't match, then create a new// session.session = manager.createSession(id, now, getSessionGroup(), this); //新建QSession对象if (session != null) //注意创建QSession对象和创建一个存储在backing store中//session不是一回事.setHasCookie();if (id != null)return session;if (manager.enableSessionCookies())getResponse().setSessionId(session.getId());return session; //返回新创建的QSession对象}。
JVM虚拟机性能调优调试JVM虚拟机性能调优调试JVM(Java虚拟机)是Java运行环境的重要组成部分,它负责将Java字节码转换为机器码并执行。
然而,在开发和部署Java应用程序时,我们经常会遇到性能瓶颈和调试问题。
本文将介绍如何进行JVM虚拟机性能调优调试,以提高Java应用程序的运行效率。
1. 监控工具的使用监控工具是进行JVM性能调优的重要工具。
JVM自带了一些常用的监控工具,例如JConsole和VisualVM。
它们可以提供关于JVM运行状态的详细信息,包括内存使用情况、线程状态和垃圾回收情况等。
通过监控工具,我们可以及时发现潜在的性能问题,并采取相应的措施进行调优。
2. 垃圾回收调优垃圾回收是JVM自动管理内存的重要机制,但不正确的垃圾回收策略可能会导致性能下降。
调优垃圾回收可以通过调整JVM的参数来实现。
例如,可以通过-Xms和-Xmx参数来调整堆内存的初始大小和最大大小,以避免频繁的垃圾回收。
另外,可以通过调整-XX:NewRatio参数来平衡新生代和老年代的比例,以减少Full GC的次数。
3. 线程管理和调优线程是Java应用程序的重要组成部分,正确地管理和调优线程可以提高应用程序的性能。
首先,应避免创建过多的线程,因为线程的创建和销毁是有开销的。
其次,可以通过调整线程池的大小来优化线程的使用。
合理地设置线程池的核心线程数和最大线程数,可以避免线程过多导致的性能下降和资源浪费。
4. JVM参数调优JVM的参数设置对应用程序的性能有很大的影响。
可以通过调整JVM的参数来优化应用程序的性能。
例如,可以通过调整-XX:MaxPermSize参数来增加永久代的大小,以避免出现PermGen Space的OOM错误。
另外,可以通过调整-XX:MaxHeapFreeRatio参数来控制堆内存的空闲比例,以减少Full GC的次数。
5. 代码优化除了调优JVM的参数和配置,代码优化也是提高应用程序性能的重要手段。
Java中的性能优化有哪些常见方法在 Java 开发中,性能优化是一个至关重要的环节。
随着应用规模的不断扩大和用户需求的日益增长,确保程序能够高效运行、快速响应变得尤为重要。
下面我们就来探讨一下 Java 中的一些常见性能优化方法。
一、算法和数据结构的选择选择合适的算法和数据结构是性能优化的基础。
例如,对于频繁的插入和删除操作,链表可能比数组更合适;对于快速查找操作,哈希表可能比线性搜索更高效。
在实际开发中,需要根据具体的业务需求和数据特点,选择最优的数据结构和算法。
比如,在一个需要快速查找元素是否存在的场景中,如果使用线性搜索,时间复杂度为 O(n),而使用哈希表,平均时间复杂度可以达到O(1)。
这将大大提高程序的执行效率。
二、减少对象创建和销毁在 Java 中,对象的创建和销毁是相对耗费资源的操作。
因此,应尽量减少不必要的对象创建和销毁。
可以通过对象复用、使用对象池等方式来实现。
例如,在一个循环中,如果每次都创建一个新的对象,会导致大量的内存分配和垃圾回收操作。
可以将对象创建放在循环外部,或者使用对象池来重复利用已经创建的对象。
另外,使用基本数据类型代替对象类型也能减少对象创建的开销。
比如,如果只需要存储一个整数,使用`int`类型而不是`Integer`对象。
三、字符串操作的优化字符串操作在很多应用中都非常常见,因此对字符串操作进行优化也能显著提升性能。
避免频繁的字符串拼接操作,因为这会创建新的字符串对象。
可以使用`StringBuilder`或`StringBuffer`类来进行字符串的拼接,它们在内部进行了优化,能够减少对象的创建。
在字符串比较时,如果不需要区分大小写,可以使用`equalsIgnoreCase()`方法,而不是先将字符串转换为小写或大写后再进行比较,这样可以减少额外的字符串转换操作。
四、合理使用缓存缓存是一种常见的性能优化手段。
可以将经常使用的数据或计算结果缓存起来,避免重复计算或重复获取数据。
jvm常用调优参数
JVM是JavaVirtualMachine的缩写,是Java程序运行的核心。
JVM的调优是优化Java应用程序性能的重要一环,其中调优参数的合理设置是关键。
以下是常用的JVM调优参数:
1. -Xms:设置JVM的初始内存大小,默认为物理内存的
1/64。
2. -Xmx:设置JVM的最大内存大小,超出该内存大小后会触发垃圾回收。
3. -Xmn:设置年轻代的大小,一般设置为总内存的1/3或
1/4。
4. -XX:SurvivorRatio:设置年轻代中Eden区和Survivor区的比例,默认值为8。
5. -XX:NewRatio:设置新生代和老年代的比例,默认值为2。
6. -XX:MaxPermSize:设置永久代的大小,一般设置为
256MB。
7. -XX:+UseConcMarkSweepGC:使用CMS垃圾回收器,可以减少内存抖动。
8. -XX:+UseParallelGC:使用并行垃圾回收器,可提高垃圾回收效率。
9. -XX:+HeapDumpOnOutOfMemoryError:当JVM内存溢出时,生成堆转储文件。
10. -XX:+PrintGCDetails:打印垃圾回收的详细信息。
以上是常用的JVM调优参数,通过合理地设置参数,可以优化Java应用程序的性能。
JVM性能优化,Part 1 ―― JVM简介众所周知,Java应用程序是运行在JVM上的,但是你对JVM有所了解么?作为这个系列文章的第一篇,本文将对经典Java虚拟机的运行机制做简单介绍,内容包括―一次编写,到处运行‖的利弊、垃圾回收的基本原理、常用垃圾回收算法的示例和编译器优化等。
后续的系列文章将会JVM性能优化的内容进行介绍,包括新一代JVM的设计思路,以及如何支持当今Java应用程序对高性能和高扩展性的要求。
如果你是一名程序员,那么毫无疑问,你肯定有过某种兴奋的感觉,就像是当一束灵感之光照亮了你思考方向,又像是神经元最终建立连接,又像是你解放思想开拓了新的局面。
就我个人来说,我喜欢这种学习新知识的感觉。
我在工作时就常常会有这种感觉,我的工作会涉及到一些JVM的相关技术,这着实令我兴奋,尤其是工作涉及到垃圾回收和JVM性能优化的时候。
在这个系列中,我希望可以与你分享一些这方面的经验,希望你也会像我一样热爱JVM相关技术。
这个系列文章主要面向那些想要裂解JVM底层运行原理的Java程序员。
文章立足于较高的层面展开讨论,内容涉及到垃圾回收和在不影响应用程序运行的情况下对安全快速的释放/分配内存。
你将对JVM的核心模块有所了解:垃圾回收、GC算法、编译器行为,以及一些常用优化技巧。
此外,还会讨论为什么对Java做基准测试(benchmark)是件很困难的事,并提供一些建议来帮助做基准测试。
最后,将会介绍一些JVM和GC的前沿技术,内容涉及到Azul的Zing JVM,IBM JVM和Oracle的Garbage First(G1)垃圾回收器。
希望在阅读此系列文章后,你能对影响Java伸缩性的因素有所了解,并且知道这些因素是如何影响Java开发的,如何使Java难以优化的。
希望会你有那种发自内心的惊叹,并且能够激励你为Java做一点事情:拒绝限制,努力改变。
如果你还没准备好为开源事业贡献力量,希望本系列文章可以为你指明方向。