草庐IT

提升 Java 应用程序的十个优化技巧

迷路的架构师 2023-03-28 原文
程序性能优化是一个复杂的话题。往往需要结合具体场景进行性能分析,找出瓶颈提出优化建议。但是,假设我们平时很少关注细节的性能,那么这种情况下,优化这些细节所带来的收益也是相当可观的。接下来,我们就来说说Java代码细节优化的一些小技巧。

复杂的字符串连接操作使用 StringBuilder

职业生涯早期,在做字符串连接操作的时候,肯定会这么写:String a=c+e+d,这个Java语法糖对于开发者来说太方便了。但是如果你在循环中使用“+”,那就得小心了。

String a=null;
for(int i=0;i<1000;i++) {
a=a+i;
}

我们都知道String 是不可变的,因此循环中对 string 的每一次赋值都会在堆内存中创建一个新的 String 对象。在一个循环体中,反复创建多个无用的对象,不仅会占用内存空间,还会影响GC时间。所以说,如果在循环中遇到字符串拼接,就使用 StringBuilder 而不是“+”

使用 ThreadPoolExecutor 避免手动创建线程

许多初学者喜欢在编写代码时创建线程,这是一种危险的做法。

如果这个线程的创建需要处理大量的请求,很可能导致你的程序频繁的创建和销毁线程,频繁的切换线程上下文,浪费CPU资源,甚至会耗尽内存。

因此,建议使用ThreadPoolExecutor,并配置合适的核心线程数和最大线程数。

为集合预分配适当的容量

我们都知道 ArrayListHashMap 和 ConcurrentHashMap 等集合类是可以自动扩容的,但是这种自动扩容涉及到底层数组的复制和迁移。如果扩容频繁,肯定会影响程序的性能。所以如果你能估计出大概的容量,请直接配置初始值。

使用枚举而不是常量类

很多人特别喜欢在项目中创建一个常量类,如下:

public class Constant {

public static final String TOKEN_HEADER = "x-request-token";

public static final Integer CODE_SUCCESS = 0;

public static final Integer CODE_REQUEST_FAILED = 1;

public static final Integer CODE_REQUEST_RUNNING = 2;
}

为什么不用枚举呢?Enum 有强制的类型验证。同时,使用枚举类的性能更高。并且使用 enum 还有更大的优势,它可以与策略模式一起使用来提高程序的可扩展性。例如:

public enum FileType {

EXCEL(".xlsx"){
@Override
public void download(String path) {
//do download excel file logic
}
}, CSV(".csv") {
@Override
public void download(String path) {
//do download csv file logic
}
};

private String suffix;

FileType(String suffix) {
this.suffix = suffix;
}

public String getSuffix() {
return suffix;
}

public abstract void download(String path);
}

如代码所示,你可以根据需要动态选择一种策略来下载文件,直接调用FileType.EXCEL.download(),无需关心代码细节。

使用 NIO 代替传统 IO

传统的 IO 已经过时了。强烈推荐使用 NIO 代替传统的 IO。因为传统IO采用阻塞IO模型,请求数据后,线程从数据准备到数据可读都是阻塞的。

而且,传统IO如果要往网卡写数据,需要先把数据写到堆内存,然后再把数据拷贝到堆外的一块内存,再从用户态拷贝数据到内核状态缓冲区。最后CPU通知DMA将数据写入网卡,一共经历了3次拷贝。NiO不仅采用了multiplex IO模型,还可以使用direct memory来减少数据拷贝次数,从而提高性能。

使用移位操作

如果你看过一些JDK的源代码,比如HashMap,你会发现代码中有很多移位操作。因为JDK是比较底层的代码,对性能的追求也是极致的。在我们日常的编码中,可以用移位运算来代替一些乘除运算,比如a >> 1 代替 a / 2a * 16 代替 a << 4

这个技巧也能在一定程度上提高性能,但是如果你不擅长,那就不要强求,因为当代计算机的性能已经非常强大了,没必要为了一个程序而牺牲代码的可读性。

尝试使用单例模式

如果我们设计一个不需要考虑线程安全的类,请用单例模式来使用这个类,这样可以节省内存。幸运的是,对于我们使用的spring框架,Java bean默认是单例的。

降低锁粒度

假设我们有一个共享文档编辑功能,用户会同时编辑共享文档。为了保证文件的正确性,我们需要使用线程安全synchronized来保证。很多初学者可能会这样写。

public class Test{
private Object lock = new Object();

public void write(String username, String fileName) {
synchronized(lock) {
//do something
}
}
}

如果采用上述方式,只有一个线程可以进入同步代码块执行,其他线程只能挂起等待,即使这些线程可能写入不同的文件。我们可以通过降低锁粒度来提高性能。

public class Test{

public void write(String username, String fileName) {
synchronized(fileName.intern()) {
//do something
}
}
}

不要随意使用静态变量

如果你熟悉JVM基础知识,那么就会知道如果一个对象被定义为静态变量,这个变量的引用就不容易被垃圾回收器回收。

public class Test{
public static A a = new A();
}

静态变量“a”的生命周期与测试类相同。只要测试类型没有被卸载,“a”的引用对象就会驻留在内存中,直到程序终止。

使用基本数据类型

在应用程序中使用基本数据类型来减少内存消耗并提高程序性能。如果可以使用 int,请不要使用其 Integer 包装类型,使用double 而不是 Double。

基本数据类型的包装类实例存放在堆内存中,每次使用都会在堆内存中创建一个。如果使用基本数据类型,数据存放在栈帧中,栈的访问速度可比堆快很多。

有关提升 Java 应用程序的十个优化技巧的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  3. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  4. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  5. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  6. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  7. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  8. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  9. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  10. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

随机推荐