在一次作业中,我们被要求使用单链表实现一个数据结构。
我的问题是,在添加项目然后删除它们之后,java程序仍然使用与以前相同的内存。
这里有一个简单的例子
Deque<Integer> deck = new Deque<>();
for( int i =0; i < 500000;i++) {
deck.addFirst(i);
}
for( int i =0; i < 500000;i++) {
deck.removeFirst();
}
System.out.println("end"); //Debugger here
添加50万个项目需要27mb内存。移除后,仍为27mb。在末尾使用调试器跳转,变量组的字段first=null,count=0;
为什么会这样?甲板是空的,没有任何项目,但内存仍像以前一样使用。
代码通过了所有的正确性测试,但是没有通过内存测试。
我还查看了分步调试器,并正在做应该做的事情。
可能是因为在removeFirst()中,我没有将任何内容设置为null,只将first赋值为first。下一个
编辑:这是内存测试的输出
Test 10: Total memory usage after inserting 4096 items, then successively
deleting items, seeking values of n where memory usage is maximized
as a function of n
n bytes
---------------------------------------------------
=> passed 3200 65592
=> passed 1600 65592
=> FAILED 800 65592 (1.7x)
=> FAILED 400 65592 (3.4x)
=> FAILED 200 65592 (6.7x)
=> FAILED 100 65592 (13.1x)
=> FAILED 50 65592 (25.3x)
==> 2/7 tests passed
如您所见,在50个元素仍在使用65592
第二版:
// remove and return the item from the front
public Item removeFirst() {
if (this.isEmpty()) throw new java.util.NoSuchElementException();
Item current = first.Item;
first = first.Next;
count--;
return current;
}
这是删除第一()
编辑3:
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
/**
* Created by Alex on 6/30/2018.
*/
public class Deque<Item> implements Iterable<Item> {
private Node first;
private int count;
private class Node {
private Node Next;
private Item Item;
}
// construct an empty deque
public Deque() {
first = null;
count = 0;
}
// is the deque empty?
public boolean isEmpty() {
if (count == 0) {
return true;
}
return false;
}
// return the number of items on the deque
public int size() {
return count;
}
// add the item to the front
public void addFirst(Item item) {
if (item == null) throw new IllegalArgumentException();
Node temp = new Node();
temp.Item = item;
temp.Next = first;
first = temp;
count++;
}
// add the item to the end
public void addLast(Item item) {
if (item == null) throw new IllegalArgumentException();
if (isEmpty()) {
addFirst(item);
} else {
Node last = first;
while (last.Next != null) {
last = last.Next;
}
Node newLast = new Node();
newLast.Item = item;
newLast.Next = null;
last.Next = newLast;
count++;
}
}
// remove and return the item from the front
public Item removeFirst() {
if (this.isEmpty()) throw new java.util.NoSuchElementException();
Item current = first.Item;
first = first.Next;
count--;
return current;
}
// remove and return the item from the end
public Item removeLast() {
if (isEmpty()) throw new java.util.NoSuchElementException();
if (size() == 1) {
return removeFirst();
}else {
Node newLast = first;
Node oldLast = first;
while (oldLast.Next != null) {
newLast = oldLast;
oldLast = oldLast.Next;
}
newLast.Next = null;
count--;
// Item lastItem = ;
return oldLast.Item;
}
}
// return an iterator over items in order from front to end
public Iterator<Item> iterator() {
return new DequeIterator();
}
private void debug() {
Iterator<Item> deckIter = iterator();
while(deckIter.hasNext()) {
System.out.println(deckIter.next());
}
System.out.println(isEmpty());
System.out.println("Size:" + size());
}
// an iterator, doesn't implement remove() since it's optional
private class DequeIterator implements Iterator<Item> {
private Node current = first;
public boolean hasNext() { return current != null; }
public void remove() { throw new UnsupportedOperationException(); }
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.Item;
current = current.Next;
return item;
}
}
// unit testing (optional)
public static void main(String[] args) throws InterruptedException {
Deque<Integer> deck = new Deque<>();
for( int i =0; i < 500000;i++) {
deck.addFirst(i);
}
for( int i =0; i < 500000;i++) {
deck.removeFirst();
}
System.out.println("end");
TimeUnit.MINUTES.sleep(5);
}
}
编辑4:这些是内存限制
https://imgur.com/nQuDfUF
谢谢你。
从我的测试来看,内存似乎仍在分配中,但正在等待垃圾回收机制。java.lang.Runtime类有一些方法来通知您内存使用情况,还有一个静态方法gc()来建议运行垃圾回收。这里有一个额外的方法和对main()
类的修改可能会对您有所帮助:
private final static long MB = 1024*1024;
static void memStats(String when)
{
Runtime rt = Runtime.getRuntime();
long max = rt.maxMemory();
long tot = rt.totalMemory();
long free = rt.freeMemory();
System.out.printf(
"mem(mb): %5d tot, %5d free %5d used %5d max %s\n",
tot/MB, free/MB, (tot-free)/MB, max/MB, when);
}
// unit testing (optional)
public static void main(String[] args) {
Deque<Integer> deck = new Deque<>();
memStats("startup");
for( int i =0; i < 500000;i++) {
deck.addFirst(i);
}
memStats("after alloc");
for( int i =0; i < 500000;i++) {
deck.removeFirst();
}
memStats("after removal");
Runtime.getRuntime().gc();
memStats("after gc");
System.out.println("end");
try {
TimeUnit.MINUTES.sleep(5);
} catch (InterruptedException ex)
{
}
}
将这些替换为Deque类中的main()并运行它。我得到:
mem(mb): 15 tot, 15 free 0 used 247 max startup
mem(mb): 29 tot, 10 free 19 used 247 max after alloc
mem(mb): 29 tot, 10 free 19 used 247 max after removal
mem(mb): 29 tot, 29 free 0 used 247 max after gc
end
如您所见,(1)分配给Java的内存从分配给Java对象的总内存15MB开始,使用了0MB。(忽略max
数字。这并没有报告我认为它做了什么。)它增加到29MB,分配后使用了19个。
在释放Deque
中的所有节点
s后,没有任何变化!
不过,在gc()调用之后,请注意,分配给堆的总内存没有改变,但现在所有内存都可以供其他Java对象使用。垃圾收集最终会发生,通常是在堆中没有足够的连续内存来满足新
请求时。
正如我在评论中所说的,我对添加到堆中的大约14MB没有返回到操作系统中并不感到惊讶。如果您在Windows中使用任务管理器,该内存仍将被视为“正在使用”。向操作系统获取内存是很昂贵的,所以通常只在大块扩展时才这样做(在这种情况下,似乎是1500万字节),并且只在运行结束时释放。不过,这是依赖于实现的行为,在不同的JVM上可能会有所不同。
关于您的代码的注释。我将节点类转换为:
private static class Node<Item> {
Node<Item> next; // lowercase next
Item item; // You were using Item for both a type and field name!?
}
…并几乎将每次出现的Node
转换为Node
这个补丁可能会帮助你通过测试。
哦,是的,请注意字段名从Next到next和Item到item的变化。如果您想适合传统的Java命名样式,请以小写字母开头字段和方法名称。在任何情况下,您都不应该同时使用Item作为字段名和泛型类型名。
用户上传一个由一百万字组成的巨大文件。我解析文件并将文件的每一行放入< code>LinkedHashMap中 我需要按键访问和删除O(1)。此外,我需要保留访问顺序,从任何位置迭代并排序。 内存消耗是巨大的。我启用了的重复数据删除功能,该功能出现在Java 8中,但事实证明,消耗了大部分内存。 我找到了< code>LinkedHashMap。Entry占用40个字节,但是只有2个指针——一个指
我所说的“空if语句”是指这样的东西(注意分号): 我在考虑这个应用程序时遇到了麻烦。使用time循环,您可以这样做: 但对于if语句没有这样的应用。此外,Java编译器在遇到此类语句时不会发出错误或警告。这可能会导致大而无声的问题,尤其是对于冗长而复杂的语句: 我的问题是:为什么Java中允许这样做?更重要的是,我是否可以启用一个选项,以便在发生这种情况时引发警告? (之前有人问过关于C#的这个
问题内容: 必须是O(n)并且是就地(空间复杂度为1)。下面的代码可以工作,但是有没有更简单或更完善的方法? 问题答案: 编辑以删除每次迭代的额外比较:
我已为单链表创建了此节点类:  我想在名为findmax的方法中搜索具有最高键的节点。但是我想检查列表是否为空,如果为空,则返回null,否则返回具有最高密钥的节点。这就是我所做的: 我只想知道我检查列表是否为空是否正确。
我在一个Redis实例中使用了几十个Redis DBs。每个DB由赋予的数字ID表示,例如: 我如何知道每个DB消耗多少内存,以及每个DB中最大的键是什么?