【问题标题】:java - creating distinct variables vs 1 variable for multiple values? [closed]java - 为多个值创建不同的变量与 1 个变量? [关闭]
【发布时间】:2019-03-08 21:46:16
【问题描述】:

我有一些类似的代码:

String country = null;
country = getEuropeanCountry(); //Germany
//after few lines of code
country = getAsianCountry(); //Japan
//after few more lines
country = getNorthAmericanCountry(); //Canada
/*and the code follows by assigning a different country value to the same variable "country"*/

我的大部分代码都有这种用法。

由于某种原因,我的应用程序抛出了"Error java.lang.OutOfMemoryError: GC overhead limit exceeded"。 所以我尝试了 VM 参数:-XX:-UseGCOverheadLimit 然后我的应用程序成功运行,但我注意到它消耗了更多内存(我必须将 -Xmx 设置为 5g 或 6g;否则我会得到:内存不足错误)。

我检查了我的应用程序,没有内存泄漏。但是我的大部分代码都有我上面发布的类似代码。

如果我将上面的代码重构为以下代码,谁能告诉我它是否有利于内存管理:

String europeanCountry = getEuropeanCountry(); //Germany
//after few lines of code
String asianCountry = getAsianCountry(); //Japan
//after few more lines
String northAmericanCountry = getNorthAmericanCountry(); //Canada
/*and the code follows by assigning a different country value to a different String variable*/

我不能使用集合。我的意思是,一般来说,哪种方式可以更好地使用堆空间和垃圾收集器?

【问题讨论】:

  • 嗯,你一定是做错了什么才会有 GC 开销限制错误。你确定你彻底检查了你的申请吗?
  • @LppEdd 你知道我应该从哪里开始我的诊断吗?
  • 这是一个棘手的问题。你的代码库有多大?
  • 它较小。包含 2 到 3 个服务类(每个最多 1000 行)和 2 到 3 个 dao 类和 1 个主类和模型类。
  • 这些方法在幕后执行了什么?

标签: java memory-management


【解决方案1】:

对于“我的意思是,一般来说,哪种方式更有效地使用堆空间和垃圾收集器?”这个问题?

让我们看看 String 的实现,例如jdk8https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/String.java

public final class String

    implements java.io.Serializable,  Comparable<String>, CharSequence {

    /** The value is used for character storage. */

    private final char value[];

所以它是一个最终的字符数组——它不能被重新签名或改变。所以它是在你的方法中的堆上生成的,并且永远不会改变 - 只生成一个引用(一个名称)。

为了正确起见,我们还要看一下 String 的构造函数(做一些事情。例如“newString = new String(otherString)”:

public String(String original) {

    this.value = original.value;

    this.hash = original.hash;

}

同样在这种情况下,堆上没有额外的空间被分配 - 它在堆上保持相同的单个最终字符数组。

因此您可以为引用分配一个新字符串。(给它一个额外的名称)。但它始终是在您的方法中生成的唯一字符串,并且不会在堆上分配新空间。

所以在第一次缩小中比较这两种方法:

String europeanCountry = getEuropeanCountry();
String asianCountry = getAsianCountry();

String country = null;
country = getEuropeanCountry();
country = getAsianCountry();

两者都会在堆上正式创建相同数量的字符串,因为字符串总是在相同的方法中生成。该变量只是对它的引用。

唯一的区别是,在第二种情况下重用允许更早地对字符串进行垃圾回收(在通过重用变量删除对它的引用的那一刻)。

因此,使用第二种方法(重用),您可以在适当的时间内生成更小的内存占用。

我在第一次缩小时说过,这仅在没有其他对 String 的引用且没有优化的情况下才是正确的 - 因此,如果不存在其他引用并且不会进行优化,则上述情况就是如此。

但是,在您上面的代码中,变量不会退出范围并且从未使用过。编译器将检测到这一点,并且根本不会分配任何变量。根据方法的作用,它们可能是内联的,也可能不会被调用。因此,您调用的方法看起来会有所不同。取决于它们的复杂程度,堆上的空间是否已分配。

反之亦然:如果您使用该变量并且运行时检测到您可能会为相同的值再次调用该方法,则该值将保留在堆上并且不会被释放,即使没有正式的无引用并且它可以正式被垃圾收集 - 所以分配再次没有区别,但方法的调用。

同样是显而易见的:如果这些方法不仅生成字符串,而且从某个地方(容器)拉出它们或将它们存储在某个地方,那么其他引用就是堆上的空间被保留(并且是)分配的那个引用,并且您的分配对堆没有任何影响:它与堆上的最终字符数组相同。

考虑到这一点,您面临的问题很可能不是字符串的分配,而是代码的设计。这一定是一个更复杂的场景,其中引用的保留时间更长。

到目前为止,您的问题。

对于你的问题,我会注意的:

  • 用于容器
  • 变量的生成位置
  • 适合频繁使用。那就是非常频繁地为许多不同的值调用方法,因为在这种情况下,它们被保存在内存中以供下一次假定的调用发生。
  • 用于不易跟踪数据流的代码。编译器通过分析流程进行优化。如果跟不上,很有可能是编译器跟不上其他部分。

【讨论】:

    【解决方案2】:

    假设显示的代码行来自单一方法(如果不是这种情况,请告诉我),我可以通过代码指出至少 3 个问题:

    1. 似乎方法大小太大。更喜欢尽可能简洁的编写方法,只做“一件事”并做好。

    2. 状态更改过多。在第一个示例中,您将变量“国家”设置为 3 个不同的方法返回值。

    3. 考虑使用 polymorphism,而不是以 if-else 方式重复代码来获取国家/地区

    最后,还不清楚国家值如何在方法中使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-31
      • 1970-01-01
      • 2022-12-19
      • 2016-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多