乐趣区

关于java:Java-内存管理最佳实践

本文翻译自国外论坛 medium,原文地址:https://medium.com/@fullstacktips/best-practices-for-memory-m…

内存治理是编程的一个根本畛域之一,尤其是在 Java 开发中。当不再须要的对象没有失去正确处理时,就会产生内存透露,导致内存使用量一直增长,最终导致性能问题和应用程序解体。因而深刻理解如何在 Java 应用程序中无效应用内存并防止内存透露至关重要。

在这篇文章中,咱们将探讨防止内存透露和优化 Java 内存应用的最佳实际。

Java 应用程序内存透露的常见起因

在深入探讨最佳实际之前,咱们首先理解 Java 应用程序中内存透露的常见起因。以下是内存透露的一些最常见起因。

  1. 循环援用:当两个或多个对象以循环形式互相援用时,就会产生内存透露。当对象没有正确开释和垃圾收集时,就会产生这种状况。
  2. 未敞开的资源:当文件句柄、数据库连贯或网络套接字等资源在应用后未正确敞开时,就会导致内存透露。
  3. 过多的对象创立:不必要地创立过多的对象也会导致内存透露。

Java 应用程序中内存治理的最佳实际

为了防止 Java 应用程序中的内存透露并优化内存应用,开发人员应该遵循这些最佳实际。

1. 应用不可变对象

不可变对象是指创立后状态无奈更改的对象。应用不可变对象能够帮忙防止循环援用引起的内存透露。不可变对象还能够通过缩小同步开销来进步性能。

例如,思考上面的类。

public final class Employee {
    private final String name;
    private final int age;
    private final Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {return name;}

    public int getAge() {return age;}

    public Address getAddress() {return address;}
}

在下面的示例中,Employee 类是不可变的,因为它的字段是 final 润饰,并且在对象创立后无奈更改。

2. 最小化对象创立

创立太多对象可能会导致内存透露。防止在循环中创建对象或者在循环中反复调用构造函数。相同尽可能地重用对象。

例如,让咱们看一下上面的代码。

String[] names = {"John", "Mary", "Steve"};

for (String name : names) {StringBuilder sb = new StringBuilder();
    sb.append("Hello");
    sb.append(name);
    sb.append("!");
    System.out.println(sb.toString());
}

正如咱们在下面的示例中看到的,在循环的每次迭代中都会创立一个新的 StringBuilder 对象。能够通过重用 StringBuilder 对象来防止这种状况,如下所示:

String[] names = {"John", "Mary", "Steve"};
StringBuilder sb = new StringBuilder();

for (String name : names) {sb.setLength(0);
    sb.append("Hello");
    sb.append(name);
    sb.append("!");
    System.out.println(sb.toString());
}

3. 应用适当的数据结构

抉择正确的数据结构能够帮忙优化内存应用。例如应用 HashMap 代替 List 能够进步搜寻特定元素时的性能。

Map<String, Employee> employees = new HashMap<>();

Employee john = new Employee("John", 30, new Address("123 Main St", "Anytown", "USA"));
Employee mary = new Employee("Mary", 35, new Address("456 Oak St", "Anytown", "USA"));

employees.put(john.getName(), john);
employees.put(mary.getName(), mary);

Employee employee = employees.get("John");

这里咱们应用 HashMap 按名称存储 Employee 对象。这使咱们可能轻松地按名称检索 Employee 对象,而无需迭代 Employee 对象列表。

4. 正确敞开资源

文件句柄、数据库连贯、网络套接字等资源在应用后正确敞开很重要,以防止内存透露。这能够应用 Java 中的 try-with-resources 语句来实现。

例如,看一下上面的代码。

try {FileInputStream fis = new FileInputStream("file.txt");
    // Do something with fis
} catch (IOException e) {e.printStackTrace();
}

在下面的例子中,FileInputStream 在应用后没有敞开,这可能会导致内存透露。内存透露。能够通过应用 try-with-resources 来防止这种状况,如下所示。

try (FileInputStream fis = new FileInputStream("file.txt")) {// Do something with fis} catch (IOException e) {e.printStackTrace();
}

在下面的代码中,FileInputStream 在被 try-with-resources 块应用后会主动敞开。

5. 应用弱援用

在 Java 中,弱援用是一种援用对象而不阻止其被垃圾收集的办法。应用弱援用进行缓存或其余须要短时间保留对象的场景。

WeakReference<MyObject> myObjectRef = new WeakReference<>(new MyObject());
MyObject myObject = myObjectRef.get(); // get the object
if (myObject != null) {// use myObject}

6. 应用 EnumSet 和 EnumMap 进行枚举

enum Color {RED, GREEN, BLUE}

// Create an EnumSet of Color values
EnumSet<Color> colorSet = EnumSet.of(Color.RED, Color.GREEN);

// Create an EnumMap of Color values
EnumMap<Color, String> colorMap = new EnumMap<>(Color.class);
colorMap.put(Color.RED, "FF0000");
colorMap.put(Color.GREEN, "00FF00");
colorMap.put(Color.BLUE, "0000FF");

在此示例中,咱们应用 EnumSet.of() 办法创立 Color 值的 EnumSet,该办法创立一个蕴含指定值的新 EnumSet。咱们还应用 EnumMap 构造函数创立 Color 值的 EnumMap,该构造函数应用指定枚举类型的键创立一个新的 EnumMap。

通过应用 EnumSet 和 EnumMap 等专用汇合,咱们能够确保应用程序无效地应用内存,并防止创立更通用汇合的开销。

7. 对大型汇合应用并行流

List<Integer> myList = new ArrayList<>();
// Add some elements to the list
...

// Set the maximum number of threads to use for the parallel stream
int maxThreads = Runtime.getRuntime().availableProcessors();
myList.parallelStream()
    .withParallelism(maxThreads)
    .filter(i -> i % 2 == 0)
    .map(i -> i * 2)
    .forEach(System.out::println);

在此示例中,咱们应用 withParallelism 办法来设置并行流要应用的最大线程数。Runtime.getRuntime().availableProcessors() 调用检索系统上可用处理器的数量,咱们应用该值作为最大线程数。

通过限度并行流应用的线程数量,咱们能够避免内存应用过多,并确保咱们的应用程序保持稳定和响应能力。

8. 更新到最新的 Java 版本

让 Java 应用程序更新至最新的 Java 版本对于 Java 的内存治理优化至关重要。这是因为每个新的 Java 版本通常都会附带对 Java 虚拟机 (JVM) 和垃圾收集器的更新和加强,这有助于改良内存治理并避免内存透露。通过放弃更新最新版本的 Java,您能够利用这些改良来确保您的应用程序安稳且最佳地运行,而不会呈现任何与内存相干的问题。

9. 定期测试和调整你的 Java 应用程序

定期测试和调整 Java 应用程序对于保护良好的内存治理实际至关重要。Java VisualVM 等剖析工具能够帮忙辨认内存应用问题和潜在的内存透露,能够通过缩小对象创立、应用高效的数据结构和正确治理援用来优化这些问题。负载和压力测试还能够发现过多的内存应用状况,从而容许进行必要的优化,例如减少 JVM 内存或缩小重负载下的对象创立。

10. 监控内存应用状况

它对于 Java 中无效的内存治理至关重要。Java VisualVM 和 JConsole 是一些能够检测内存透露、执行堆转储并提供无关 Java 堆的详细信息(包含对象计数)的工具。

总结

在这篇文章中,咱们探讨了防止内存透露和优化 Java 内存应用的最佳实际。通过遵循这些实际,开发人员能够进步 Java 应用程序的性能和可靠性。请记住应用不可变对象、最小化对象创立、应用适当的数据结构并正确敞开资源以防止内存透露。

关注公众号【waynblog】每周分享技术干货、开源我的项目、实战经验、国外优质文章翻译等,您的关注将是我的更新能源!

退出移动版