• Java性能优化,操作系统内核性能调优,JYM优化,Tomcat调优


    文章目录

    Java性能优化

    尽量在合适的场合使用单例

    使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:

    • 第一,控制资源的使用,通过线程同步来控制资源的并发访问;
    • 第二,控制实例的产生,以达到节约资源的目的;
    • 第三,控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信。

    尽量避免随意使用静态变量

    当某个对象被定义为static变量所引用,那么GC通常是不会回收这个对象所占有的内存,如

    public class A{
    	private static B b = new B();
    }
    

    此时静态变量b的生命周期与A类同步,如果A类不会卸载,那么b对象会常驻内存,直到程序终止。

    尽量避免过多过常地创建Java对象

    尽量避免在经常调用的方法,循环中new对象,由于系统不仅要花费时间来创建对象,而且还要花时间对这些对象进行垃圾回收和处理,在我们可以控制的范围内,最大限度地重用对象,最好能用基本的数据类型或数组来替代对象。

    尽量使用final修饰符

    带有final修饰符的类是不可派生的。在JAVA核心API中,有许多应用final的例子,例如java、lang、String,为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关),此举能够使性能平均提高50%。

    如:让访问实例内变量的getter/setter方法变成”final:

    简单的getter/setter方法应该被置成final,这会告诉编译器,这个方法不会被重载,所以,可以变成”inlined”,例子:

    在这里插入图片描述

    尽量使用局部变量

    调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快;其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。

    尽量处理好包装类型和基本类型两者的使用场所

    虽然包装类型和基本类型在使用过程中是可以相互转换,但它们两者所产生的内存区域是完全不同的,基本类型数据产生和处理都在栈中处理,包装类型是对象,是在堆中产生实例。在集合类对象,有对象方面需要的处理适用包装类型,其他的处理提倡使用基本类型。

    慎用synchronized,尽量减小synchronize的方法

    都知道,实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。synchronize方法被调用时,直接会把当前对象锁了,在方法执行完之前其他线程无法调用当前对象的其他方法。所以,synchronize的方法尽量减小,并且应尽量使用方法同步代替代码块同步。

    尽量不要使用finalize方法

    实际上,将资源清理放在finalize方法中完成是非常不好的选择,由于GC的工作量很大,尤其是回收Young代内存时,大都会引起应用程序暂停,所以再选择使用finalize方法进行资源清理,会导致GC负担更大,程序运行效率更差。

    尽量使用基本数据类型代替对象

    String str = "hello";
    

    上面这种方式会创建一个“hello”字符串,而且JVM的字符缓存池还会缓存这个字符串;

    String str = new String("hello");
    

    此时程序除创建字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o

    多线程在未发生线程安全前提下应尽量使用HashMap、ArrayList

    HashTable、Vector等使用了同步机制,降低了性能。

    尽量合理的创建HashMap

    当你要创建一个比较大的hashMap时,充分利用这个构造函数

    public HashMap(int initialCapacity,float loadFactor);
    

    尽量减少对变量的重复计算

    如:

    for (int i=0;i<list.size();i++)
    

    应该改为:

    for(int i=0;len=list.size();i<len;i++)
    

    尽量避免不必要的创建

    如:

    A a = new A();
    if(i==1){
    	list.add(a);
    }
    

    应该改为

    if(i==1){
    	A a = new A();
    	lista.add(a);
    }
    

    尽量在finally块中释放资源

    程序中使用到的资源应当被释放,以避免资源泄漏,这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

    尽量使用移位来代替’a/b’的操作

    "/"是一个代价很高的操作,使用移位的操作将会更快和更有效.

    如:

    int num = a/4;
    int num = a/8;
    

    应该改为:

    int num = a>>2;
    int num = a>>3;
    

    尽量使用移位来代替’a*b’的操作

    同样的,对于’*'操作,使用移位的操作将会更快和更有效

    如:

    int num = a*4;
    

    改为:

    int num = a<<2;
    

    尽量确定StringBuffer的容量

    StringBuffer 的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建 StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。

    如:

    StringBuffer buffer = new StringBuffer(1000);
    

    尽量早释放无用对象的引用

    尽量避免使用split

    除非是必须的,否则应该避免使用split,split由于支持正则表达式,所以效率比较低,如果是频繁的几十,几百万的调用将会耗费大量资源,如果确实需要频繁的调用split,可以考虑使用apache的StringUtils.split(string,char),频繁split的可以缓存结果。

    ArrayList & LinkedList

    一个是线性表,一个是链表,一句话,随机查询尽量使用ArrayList,ArrayList优于LinkedList,LinkedList还要移动指针,添加删除的操作LinkedList优于ArrayList,ArrayList还要移动数据,不过这是理论性分析,事实未必如此,重要的是理解好2者得数据结构,对症下药。

    尽量使用System.arraycopy ()代替通过来循环复制数组

    尽量缓存经常使用的对象

    尽可能将经常使用的对象进行缓存,可以使用数组,或HashMap的容器来进行缓存,但这种方式可能导致系统占用过多的缓存,性能下降,推荐可以使用一些第三方的开源工具,如EhCache,Oscache进行缓存,他们基本都实现了FIFO/FLU等缓存算法。

    慎用异常

    当创建一个异常时,需要收集一个栈跟踪(stack track),这个栈跟踪用于描述异常是在何处创建的。构建这些栈跟踪时需要为运行时栈做一份快照,正是这一部分开销很大。当需要创建一个 Exception 时,JVM 不得不说:先别动,我想就您现在的样子存一份快照,所以暂时停止入栈和出栈操作。栈跟踪不只包含运行时栈中的一两个元素,而是包含这个栈中的每一个元素。

    如果您创建一个 Exception ,就得付出代价,好在捕获异常开销不大,因此可以使用 try-catch 将核心内容包起来。从技术上讲,你甚至可以随意地抛出异常,而不用花费很大的代价。招致性能损失的并不是 throw 操作——尽管在没有预先创建异常的情况下就抛出异常是有点不寻常。真正要花代价的是创建异常,幸运的是,好的编程习惯已教会我们,不应该不管三七二十一就抛出异常。异常是为异常的情况而设计的,使用时也应该牢记这一原则。

    尽量重用对象

    特别是String对象的使用中,出现字符串连接情况时应使用StringBuffer代替,由于系统不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理。因此生成过多的对象将会给程序的性能带来很大的影响。

    不要重复初始化变量

    默认情况下,调用类的构造函数时,java会把变量初始化成确定的值,所有的对象被设置成null,整数变量设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键字创建一个对象时,构造函数链中的所有构造函数都会被自动调用。

    这里有个注意,给成员变量设置初始值但需要调用其他方法的时候,最好放在一个方法。比如initXXX()中,因为直接调用某方法赋值可能会因为类尚未初始化而抛空指针异常,如:public int state = this.getState()。

    过分的创建对象会消耗系统的大量内存,严重时,会导致内存泄漏,因此,保证过期的对象的及时回收具有重要意义。JVM的GC并非十分智能,因此建议在对象使用完毕后,手动设置成null。

    在使用同步机制时,应尽量使用方法同步代替代码块同步。

    不要在循环中使用Try/Catch语句,应把Try/Catch放在循环最外层

    Error是获取系统错误的类,或者说是虚拟机错误的类。不是所有的错误Exception都能获取到的,虚拟机报错Exception就获取不到,必须用Error获取。

    不用new关键字创建对象的实例

    用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。

    下面是Factory模式的一个典型实现:

    public static Credit getNewCredit(){
    	return new Credit();
    }
    

    改进后的代码使用clone()方法:

    private static Credit BaseCredit = new Credit();
    public static Credit getNewCredit(){
    	return (Credit)BaseCredit.clone();
    }
    

    不要将数组声明为:public static final

    HaspMap的遍历:

    在这里插入图片描述

    利用散列值取出相应的Entry做比较得到结果,取得entry的值之后直接取key和value。

    array(数组)和ArrayList的使用

    array 数组效率最高,但容量固定,无法动态改变,ArrayList容量可以动态增长,但牺牲了效率。

    StringBuffer,StringBuilder的区别在于

    java.lang.StringBuffer 线程安全的可变字符序列。一个类似于String的字符串缓冲区,但不能修改。StringBuilder与该类相比,通常应该优先使用StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。

    为了获得更好的性能,在构造StringBuffer或StringBuilder时应尽量指定她的容量。当然如果不超过16个字符时就不用了。 相同情况下,使用StringBuilder比使用StringBuffer仅能获得10%~15%的性能提升,但却要冒多线程不安全的风险。综合考虑还是建议使用StringBuffer。

    尽量使用基本数据类型代替对象。

    应尽可能避免使用内在的GET,SET方法。

    避免枚举,浮点数的使用。

    操作系统内核优化

    net.ipv4.tcpmaxtwbuckets = 6000 
    timewait 的数量,默认是180000。
    net.ipv4.iplocalportrange = 1024 65000 
    允许系统打开的端口范围。
    net.ipv4.tcptwrecycle = 1 
    启用timewait 快速回收。
    net.ipv4.tcptwreuse = 1 
    开启重用。允许将TIME-WAIT sockets 重新用于新的TCP 连接。
    net.ipv4.tcpsyncookies = 1 
    开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理。
    net.core.somaxconn = 262144 
    web 应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGXLISTENBACKLOG默认为511,所以有必要调整这个值。
    net.core.netdevmaxbacklog = 262144 
    每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
    net.ipv4.tcpmaxorphans = 262144 
    系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,故而连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。
    net.ipv4.tcpmaxsynbacklog = 262144 
    记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。
    net.ipv4.tcptimestamps = 0 
    时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。
    net.ipv4.tcpsynackretries = 1 
    为了打开对端的连接,内核需要发送一个SYN 并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。
    net.ipv4.tcpsynretries = 1 
    在内核放弃建立连接之前发送SYN 包的数量。
    net.ipv4.tcpfintimeout = 1 
    如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2内核的通常值是180秒,3你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN-WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。
    net.ipv4.tcpkeepalive_time = 30 
    当keepalive 起用的时候,TCP发送keepalive消息的频度。缺省是2小时。
    内核参数优化设置在/etc/sysctl.conf文件中。
    

    jvm层面优化:

    JDK7:

    Xms2G
    Xmx2G
    Xmn512m
    PermSize=512M
    MaxPermSize=512M
    XX:+UseConcMarkSweepGC
    XX:+CMSClassUnloadingEnabled
    XX:+HeapDumpOnOutOfMemoryError
    verbose:gc 
    XX:+PrintGCDetails
    XX:+PrintGCTimeStamps
    XX:+PrintGCDateStamps
    Xloggc:/appl/gc.log 
    XX:CMSInitiatingOccupancyFraction=75
    XX:+UseCMSInitiatingOccupancyOnly
    

    JDK8:

    Xms2G
    Xmx2G
    Xmn512m
    XX:MetaspaceSize=512M
    XX:MaxMetaspaceSize=512M
    XX:+UseConcMarkSweepGC
    XX:+CMSClassUnloadingEnabled
    XX:+HeapDumpOnOutOfMemoryError
    verbose:gc 
    XX:+PrintGCDetails
    XX:+PrintGCTimeStamps
    XX:+PrintGCDateStamps
    Xloggc:/appl/gc.log 
    XX:CMSInitiatingOccupancyFraction=75
    XX:+UseCMSInitiatingOccupancyOnly
    
    

    Tomcat调优

    maxThreads - Tomcat线程池最多能起的线程数
    maxConnections - Tomcat最多能并发处理的请求(连接)
    acceptCount - Tomcat维护最大的对列数
    minSpareThreads - Tomcat初始化的线程池大小或者说Tomcat线程池最少会有这么多线程。 比较容易弄混的是maxThreads和maxConnections这两个参数:

    maxThreads是指Tomcat线程池做多能起的线程数
    maxConnections则是Tomcat一瞬间做多能够处理的并发连接数。比如maxThreads=1000,maxConnections=800,假设某一瞬间的并发时1000,那么最终Tomcat的线程数将会是800,即同时处理800个请求,剩余200进入队列“排队”,如果acceptCount=100,那么有100个请求会被拒掉。
    注意:根据前面所说,只是并发那一瞬间Tomcat会起800个线程处理请求,但是稳定后,某一瞬间可能只有很少的线程处于RUNNABLE状态,大部分线程是TIMED_WAITING,如果你的应用处理时间够快的话。所以真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。

    tt

    
    //容器启动时进行,具体可参考org.apache.catalina.util.LifecycleBase#startInternal()
    
    @Override
    
    protected void startInternal() throws LifecycleException{
    	//实例化任务队列  
        taskqueue = new TaskQueue(maxQueueSize);
        //自定义的线程工厂类,实现了JDK的ThreadFactory接口 
    	TaskThreadFactory tf =new TaskThreadFactory(namePrefix,daemon,getThreadPriority());	
    	//这里的ThreadPoolExecutor是tomcat自定义的,不是JDK的ThreadPoolExecutor
    	executor =new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime,TimeUnit.MILLISECONDS,taskqueue, tf);
    	executor.setThreadRenewalDelay(threadRenewalDelay);
    	//是否提前启动线程,如果为true,则提前初始化minSpareThreads个的线程,放入线程池内
    	if(prestartminSpareThreads){
    	    executor.prestartAllCoreThreads(); 
        }
        //设置任务容器的父级线程池对象
        taskqueue.setParent(executor);
        //设置容器启动状态
        setState(LifecycleState.STARTING);
    }   
    

    ttt

    //JDK默认操作线程的方法,参考java.util.concurrent.Executor接口
    @Override
    public void execute(Runnable command) {
    	if( executor != null ) {
    		 try{
    			 executor.execute(command);
    			}catch (RejectedExecutionException rx) {
    				//there could have been contention around the queue
    				if(!(TaskQueue) executor.getQueue()).force(command)) throw new RejectedExecutionException("Work queue full.");
    			}			 
    		 else  throw new IllegalStateException("StandardThreadPool  not started");
    }
    
    //由于继承了org.apache.tomcat.util.threads.ResizableExecutor接口,所以可以重新定义线程池的大小
    @Override
    public boolean resizePool(int corePoolSize,int maximumPoolSize){
    	if( executor  == null )
    		return false;
    		
    	executor.setCorePoolSize(corePoolSize);
    	executor.setMaximumPoolSize(maximumPoolSize);
    	return true;
    }
    
    

    Tomcat的线程池的名字也叫作ThreadPoolExecutor。

  • 相关阅读:
    Python元组、列表、字典
    测试通过Word直接发布博文
    Python环境搭建(windows)
    hdu 4003 Find Metal Mineral 树形DP
    poj 1986 Distance Queries LCA
    poj 1470 Closest Common Ancestors LCA
    poj 1330 Nearest Common Ancestors LCA
    hdu 3046 Pleasant sheep and big big wolf 最小割
    poj 3281 Dining 最大流
    zoj 2760 How Many Shortest Path 最大流
  • 原文地址:https://www.cnblogs.com/aixing/p/13327516.html
Copyright © 2020-2023  润新知