Java-创建不同的变量与多个值的1个变量?

时间:2019-03-08 21:46:17

标签: java memory-management

我有一些类似的代码:

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*/

我不能使用收藏集。我的意思是,总的来说,哪种方法更有效地使用堆空间和垃圾收集器更好?

2 个答案:

答案 0 :(得分:1)

对于“我的意思是,通常来说,哪种方式更有效地使用堆空间和垃圾回收器更好?”

让我们看一下String的实现,例如jdk8 https://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;

}

在这种情况下,也不会在堆上分配额外的空间-它在堆上保留相同的单个最终char数组。

因此您可以将新的String分配给引用。(给它一个附加名称)。但这总是与您的方法中生成的唯一字符串相同,并且堆上没有分配新空间。

因此首先比较一下这两种方法:

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

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

两者都将在形式上在堆上创建相同数量的String,就像在相同方法中始终生成String一样。该变量仅是对此的引用。

唯一的区别是,第二种情况下的重用允许形式上的String被更早地进行垃圾收集(此刻,通过重用该变量来删除对它的引用)。

因此,通过第二种方法(重用),您可以在可忽略的时间内生成较小的内存占用。

我首先说过,因为只有在没有其他对String的引用且没有优化的情况下,这才是正确的-因此,如果不存在其他引用并且不进行任何优化,上述情况就是如此。

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

反之亦然:如果使用变量,并且运行时检测到您可能会再次针对相同的值调用该方法,则该值将保留在堆中,即使没有正式的无引用也不会释放该值。可能会被正式收集为垃圾-因此,再次调用除了方法的调用外没有其他区别。

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

考虑到这一点,您面临的问题很可能不是字符串的分配,而是代码的设计。它必须是一个更为复杂的方案,在该方案中引用保留的时间更长。

到目前为止,您的问题。

  

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

     
      
  • 用于容器
  •   
  • 生成变量的地方
  •   
  • 频繁使用。那就是非常频繁地为许多不同的值调用方法,因为在这种情况下,它们会保存在内存中,以便进行下一个假定的调用。
  •   
  • 用于不容易遵循数据流的代码。编译器通过分析流程进行优化。如果您不能遵循,那么编译器比其他部分更不能做到这一点。
  •   

答案 1 :(得分:0)

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

  1. 似乎方法大小太大。宁愿写法尽可能简洁,只做“一件事”并做好。

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

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

最后,尚不清楚方法中如何使用国家/地区值。