Java 为堆栈上的对象而不是堆上的对象预分配内存
以c中的LinkedList为例,我在堆栈上显式地预先分配了N个节点结构,用作节点池或节点堆栈,而不是使用慢速mallocs和frees,我不需要在运行中释放节点的功能,因此堆栈可以:Java 为堆栈上的对象而不是堆上的对象预分配内存,java,memory,new-operator,Java,Memory,New Operator,以c中的LinkedList为例,我在堆栈上显式地预先分配了N个节点结构,用作节点池或节点堆栈,而不是使用慢速mallocs和frees,我不需要在运行中释放节点的功能,因此堆栈可以: #define N 40000 typedef struct node_t { void * ele; struct node_t * next; }node,*Pnode; node Stack[N];//memory allocation for the linkedlist nodes
#define N 40000
typedef struct node_t {
void * ele;
struct node_t * next;
}node,*Pnode;
node Stack[N];//memory allocation for the linkedlist nodes
int sp=0;
Pnode createNode(void * x) {
Pnode temp=&Stack[sp++];
temp->ele=x;
temp->next=NULL;
return temp;
}
当我试图把上面的想法移植到JAVA中时,我想到了这个。。。您能否完成该类以使Node[]堆栈成为一个nodes对象数组,该对象的内存在堆栈中预先分配
public class Node<E> {
private final static int n = 40000;
private static Node[] stack = ?
private static int sp = 0;
private E ele;
private Node next;
private Node () {}
public Node createNode(E e) {
stack[sp].ele=e;
stack[sp].next=null;
return stack[sp++];
}
}
基本上,我想知道这一点,因为我知道我希望我的程序能够做什么,我知道我不需要释放和重用内存块的能力,我希望能够快速分配节点对象,即使我的程序几乎出现堆溢出。最大容量为N的节点堆栈和像我这样运行的索引非常适合我…否。Java没有在堆栈上分配内存的显式机制 使用new是分配内存的唯一方法。然后由JVM决定内存的来源,并从那时起对其进行管理 编辑:我刚刚仔细看过你的代码。您甚至没有在堆栈上分配内存。您所做的似乎是拥有一个位于数据段上的堆栈数据结构,并且您可以自己管理它 在Java中,没有直接的等价物:
node Stack[N];
换句话说,无法在一个连续的内存块中构造N个对象。您必须分配一个包含N个引用的数组,然后创建N个对象
话虽如此,但请记住,在现代JVM中,new本质上相当于指针碰撞。这是:便宜;b与您对sp的操作类似
您能否完成该类以使Node[]成为堆栈中的节点数组对象
不需要。Java根据自己的启发式为您管理内存分配。据我所知,JLS中没有任何东西可以保证特定对象将被分配到何处
我的问题是-为什么要在堆栈上分配它?根据垃圾收集器的内部状态,您认为您比Hotspot更了解此数据的最佳位置吗?基于我公认的非专家知识,无论如何,这些对象最好放在堆上的伊甸园池中
使用Java需要学习的一件事就是放手,相信VM关于在何处分配内存的决定。只要您避免编写复杂的算法,它通常会在这些决策方面做得非常好,通常比开发人员做得更好,因为它在编写代码时会考虑运行时可用的信息,而不是被迫采用静态二分法。您似乎想要的是一个对象池 一个简单的池就是使用ArrayList
public class Node<E> {
private final static int MAX_SIZE = 40000;
private final static List<Node> freeNodes = new ArrayList<>();
private E ele;
private Node next;
private Node () {}
public static Node<E> acquireNode(E e) {
Node node = freeNodes.size() > 0
? freeNodes.remove(freeNodes.size()-1)
: new Node();
node.ele = e;
return node;
}
public static void freeNode(Node<E> node) {
if(freeNodes.size() < MAX_SIZE) {
node.next = null;
freeNodes.add(node);
}
}
}
我建议使用不同的结构,例如ArrayList或RingBuffer,而不是使用绑定到创建大量对象的LinkedList,因为它们都不需要这些节点。做某事最快的方法就是根本不做 使用new是动态分配内存的唯一方法,但我并没有尝试动态分配内存,那么有可能吗?@OfekRon:正如我所说,没有机制强制JVM在堆栈上分配内存。@您的编辑,有什么不同?据我所知,如果我错了,所有变量都会纠正我,因为你在程序中声明的是在堆栈上分配内存。由compiler@OfekRon:在我看来,您过早地进行了优化。编写代码,分析结果,如果速度太慢,请用精确的基准测试呈现您的特定代码。然后我们就可以给你具体的建议,如何使它更快。@OfekRon:正如我一直在解释的,C结果不会自动应用于Java。在我的电脑上,分配5000万个像你这样的对象并将它们存储在Java数组中需要大约10秒钟。无论如何,在我们看到一些实际的代码和基准测试之前,这个讨论不会真正深入。这是因为我知道我希望我的程序能够做什么,我知道我不需要释放和重用内存块的能力,我希望能够像Lighting一样快速分配节点对象,即使我的程序几乎出现堆溢出。。。当我分配越来越多的节点时,我的程序变慢了,当我使用malloc而不是仅仅预先分配节点时,上面的C代码也出现了同样的情况。您是否使用了探查器或-verbose:gc来确定慢度取决于分配给新对象的内存位置?一般来说,即使堆几乎满了,在没有碎片的情况下,分配所需的内存块也应该是一个固定的时间操作,这里就是这种情况。我尽量避免使用LinkedList,特别是在java中,但例如id宁愿合并20亿个按链接l排序的节点
列表中有一个40亿个节点排序的链表,然后数组也一样。你在那里做的不算,也不做我想做的,因为在数组列表中,没有任何内容,我必须用堆上的new分配的新节点填充arraylist,这不是我想要的。如果你有一个60亿个节点的链表,你将有大约144 GB的对象开销,仅仅是对象的节点+16字节头大约32字节。如果你有数十亿个对象/记录要合并,或者你不能在Java中一次创建多个对象,我建议使用堆外内存。数组是引用或基本体的数组,而不是对象。这是你能得到的最接近的。