当前位置: 首页 > 知识库问答 >
问题:

Java内存分配

郎曜文
2023-03-14

我有一个一直在思考的问题。以这个特殊的类为例

class A{

private static ArrayList<String> listOne;

public Static ArrayList<String> getList()
  {
    return this.listOne
  }
}  

假设我有一个B类,它拥有一个使用listOne读取详细信息的方法。要查看数组列表,我需要首先获取列表的大小,以便我的代码知道数组列表何时结束。有两种方法可以做到这一点,一种是

int listSize = A.getList().size()
for(int count =0; count < listSize; count++)
{
  // code to read through arraylist
}

或者我也可以用

for(int count=0; count < A.getList().size(); count++)
{
  // code to read through arraylist
}

在内存和效率方面,哪种方法更好?此外,假设我正在递归地读取一个非常大的数组。为了简单起见,让我们假设递归读取此数组将导致堆栈溢出异常。在这种情况下,第一个方法在理论上会导致堆栈溢出发生的时间早于第二个方法,因为每个递归调用的堆栈帧都必须保持变量“listSize”的状态吗?

共有3个答案

帅德惠
2023-03-14

关于哪种方法更有效,我认为它们不会有任何明显的差异。正如Germann所说,根据JVM的不同,编译器甚至会对此进行优化。所以不要担心这种微不足道的差异。

我个人将使用第二种方法,因为它的代码行较少,而且我很懒。。。

但是,为什么两者都不用呢?

有一个超级酷的选择,它的名字是。。。

让我们将其与正常的for循环进行比较:

正常:

for (int i = 0 ; i < A.getList().size() ; i++ {

}

增强型:

for (String item : A.getList()) {
    // Instead of using A.getList().get(i) to access the items, just use "item"!
}

看这多好啊!

这两个for循环之间的主要区别是

  • 正常的for循环只是一种带有初始化和增量的time循环。
  • 增强的for循环调用. iterator(). hasNext(). iterator(). Next()循环。
  • 您需要知道列表的大小才能使用法线for循环
  • 您的列表只需要实现Iterable和可能的Iterator即可使用增强的for循环。不需要大小。

增强的for循环有以下限制:

  • 您不能同时遍历两个数组
  • 如果您想知道列表的索引,最好使用普通的for循环,因为多次调用indexOf不是很有效。
华恩
2023-03-14

两个循环相同。第二个是更好的编码,因为它减少了代码行。既然您提到需要遍历列表,那么最好使用增强的(for each)for循环。

Java中增强的循环和迭代器的优势是什么?

为什么增强for loop比普通for loop效率高

寿飞飙
2023-03-14

查看javap-verbose的结果:

 0: invokestatic  #2                  // Method A.getList:()Ljava/util/ArrayList;
 3: invokevirtual #3                  // Method java/util/ArrayList.size:()I
 6: istore_1
 7: iconst_0
 8: istore_2
 9: iload_2
10: iload_1
11: if_icmpge     27
14: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_2
18: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
21: iinc          2, 1
24: goto          9
27: iconst_0
28: istore_2
29: iload_2
30: invokestatic  #2                  // Method A.getList:()Ljava/util/ArrayList;
33: invokevirtual #3                  // Method java/util/ArrayList.size:()I
36: if_icmpge     52
39: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
42: iload_2
43: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
46: iinc          2, 1
49: goto          29
52: return

第一种情况是:

 9: iload_2
10: iload_1
11: if_icmpge     27
14: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_2
18: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
21: iinc          2, 1
24: goto          9

第二个:

29: iload_2
30: invokestatic  #2                  // Method A.getList:()Ljava/util/ArrayList;
33: invokevirtual #3                  // Method java/util/ArrayList.size:()I
36: if_icmpge     52
39: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
42: iload_2
43: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
46: iinc          2, 1
49: goto          29

如您所见,它将在每次循环迭代期间获取列表及其大小。但是,这可能会被JIT优化,因此仅从编译的字节码来看结果并不明显。

创建自:

import java.io.*;
import java.util.*;

public class Z {

    public static void main(String[] args) throws Exception {
        int listSize = A.getList().size();
        for(int count =0; count < listSize; count++) {
            System.out.println(count);
        }
        for(int count =0; count < A.getList().size(); count++) {
            System.out.println(count);
        }
    }

}

class A{

    private static ArrayList<String> listOne = new ArrayList<>(Arrays.asList("1", "2", "3"));

    public static ArrayList<String> getList()
    {
        return listOne;
    }
}  
 类似资料:
  • 我有一个非常消耗内存的程序,想使用-Xmx2048m为java jvm分配更多的最大内存。但在启动时,我得到一个“无法为对象堆保留足够的空间”错误。 当尝试多个值并发现我的最大值是时,它就开始了。但我喜欢分配2GB。在使用-Xmx1560m启动java进程后,我有6GB的物理内存,Taskmanager显示3400MB可用。 有人知道我为什么不能分配2GB吗?

  • 问题内容: 我读过某个地方,Java可以在大约12条机器指令中为对象分配内存。这对我来说非常令人印象深刻。据我了解,JVM使用的技巧之一是按块预分配内存。我认为,这有助于最大程度地减少对操作系统的请求数量,这是非常昂贵的。但是,即使是CAS操作,在现代处理器上也可能要花费多达150个周期。 那么,谁能在Java中解释内存分配的实际成本以及JVM使用哪些技巧来加快分配速度? 问题答案: JVM为每个

  • 问题内容: 使用以下Java选项启动Apache Tomcat(Atlassian Confluence)实例: 但是,我看到启动后,它很快就耗尽了虚拟服务器上可用的1GB内存中的大部分。 总消耗的内存(堆+ PermGen)是否不应该保持在使用- Xmx指定的值以下?这引起的问题之一是我无法使用关闭脚本关闭服务器,因为它试图生成具有256MB内存的JVM,该JVM因不可用而失败。 问题答案: T

  • 问题内容: 是局部变量,将其存储在堆或堆栈中的何处? 问题答案: 在堆上。每当您用来创建对象时,它都会在堆上分配。

  • 问题内容: 类B继承了类A。现在,当我们创建类型B的对象时,为B分配的内存是多少?是否包括A和B或任何其他内存分配过程? 问题答案: 当创建对象B时,假设调用了默认构造函数 然后,JVM分配具有更多或更少内容的对象: 在B中显式声明的每个字段都有足够的内存(每个字段通常大约4-8字节,但是类型和主机系统之间有很大差异) 对于A及其祖先继承的每个最终字段,都有足够的内存 足够的内存来包含对调度向量的

  • 我有一个关于java如何处理未使用变量的问题。 假设我有以下代码: 那么我就不会在代码中使用notUsedVariable。该变量是否会被存储,或者java是否足够聪明,可以在编译时忽略该变量? 谢谢