Java for 循环中的卡内存泄漏?

Java Card memory leak in for loop?

我知道 Java Card VM 没有垃圾收集器,但是 for 循环会发生什么:

for(short x=0;x<10;x++)
{}

x 变量是在 for 循环后得到利用,还是变成垃圾?

以防万一我有一个大小为 2 的名为 index 的瞬态字节数组(而不是 for 循环中的 i)并且我在 [=13= 中使用该数组]循环:

for(index[0]=0;index[0]<10;index[0]++)
{}

但是比第一个版本慢了一点。如果我在 for 循环中为索引使用普通变量,那么它会变得非常慢。

那么,第一个 for 循环中的 x 变量发生了什么?这样使用 for 循环是否安全?

让我们简单介绍一下内存。简而言之,智能卡中有以下3种类型的记忆:

  • ROM(有时是 FLASH)
  • EEPROM
  • 内存

ROM:

卡的 OS 和 Java 卡 API 和一些工厂专有包存储在这里。这个内存的内容是固定的,你不能修改它。在芯片生产中写入此内存仅发生一次,该过程被命名为 Masking.

EEPROM:

这是您的小程序加载到的可修改内存,它由 4 个部分组成,名称如下:

  1. 文本:也称为代码段,包含程序的机器指令。代码可以被认为是小说的文本:它讲述了程序做什么的故事
  2. Data : 包含程序的静态数据,即在整个程序执行过程中存在的变量。 C 或 C++ 程序中的全局变量是静态的,在 C、C++ 或 Java.
  3. 中声明为静态的变量也是如此
  4. Heap : 是一个内存池,用于动态分配内存,例如 C 中的 malloc() 或 C++ 中的 new 和 Java.
  5. 堆栈:包含系统堆栈,用作临时存储。

断电(比如撕卡)对这段内存的内容没有任何影响。

内存:

这也是一种可修改的内存类型。 RAM 和 EEPROM 之间的三个主要区别:

  1. RAM 确实比 EEPROM 快。 (快 1000 倍)
  2. RAM 的内容会在断电时被破坏。
  3. EEPROM的写入次数有限(一般为100.000次),而RAM的写入次数要多一些。

然后呢?

当你写for(short x=0; x<10; x++)时,你将x定义为局部变量。局部变量存储在 Stack 中。堆栈指针将在掉电时重置,并且已使用的堆栈部分将被回收。所以Garbage Collector缺失的主要问题是关于Heap.

即,当您使用 new 关键字定义局部变量时,您将堆的那部分永远指定为局部变量。当运行时环境完成该方法时,该对象将销毁并变得不可用,而堆的那部分不会被回收。所以你会失去那部分Heap。您用于 for 循环的案例似乎没问题,并且不会造成任何问题,因为您没有使用 new 关键字。

请注意,在较新版本的 Java Card(2.2.2 及更高版本)中,有一个手动垃圾收集器(查看 JCSystem.requestObjectDeletion documentation). But consider that it is really slow and even dangerous in some situations(Look 问题)。

x变量在字节码中并不真正存在。 Java 堆栈中的某个位置表示 x(无论是 Java 字节码还是 Java 卡转换器转换后的代码)。现在 Java 堆栈是一个虚拟堆栈。该堆栈是在具有寄存器和非虚拟堆栈的 CPU 上实现的。通常,如果有足够的寄存器可用,那么变量 x 会简单地放入一个寄存器中,直到它超出范围。寄存器当然可以重复使用。 CPU 堆栈本身是瞬态存储器 (RAM) 中的 LIFO(后进先出)队列。在执行构成您的 Applet 的字节码期间,堆栈会不断增长和收缩。像寄存器一样,堆栈内存被一遍又一遍地重复使用。所有局部变量(在代码块和方法参数中定义的变量)都以这种方式处理。

如果将变量放在瞬态数组中,那么就是将变量放在基于 RAM 的堆上。 Java 卡 RAM 堆永远不会超出范围。这意味着如果您更新需要将更改写入临时内存的值。正如您通过实验发现的那样,这当然比 CPU 寄存器的本地化更新慢。通常瞬态内存中的内存永远不会被释放。也就是说,您当然可以将内存重用于其他目的,只要您拥有对数组的引用即可。请注意,引用本身(index[0] 中的 index)可能在持久内存(EEPROM 或闪存)或临时内存中。

不清楚 "normal variable" 是什么意思。如果这是用 new 创建的东西,或者如果它是对象实例中的 字段 ,那么它会保留在持久内存(EEPROM 或闪存)的堆中。 EEPROM 和闪存的写入周期 数量有限,写入EEPROM 或闪存比写入RAM 慢


Java 卡片包含两种暂存:CLEAR_ON_RESET和CLEAR_ON_DESELECT。两者的区别在于CLEAR_ON_RESET允许内存在Applet实例之间共享,而CLEAR_ON_DESELECT允许内存被不同的Applet重用。

Java Card classic 不包含在 Applet 执行期间运行的垃圾收集器,您通常只能在启动期间使用 JCSystem.requestObjectDeletion() 请求垃圾收集,这将清理未引用的内存不再,无论是在瞬时内存中的堆上还是在持久内存中。清理内存就是扫描所有内存,标记所有未被引用的块,然后压缩内存。这类似于对硬盘进行碎片整理;这可能需要很长时间。

ROM 在制造阶段被填充。它可能包含操作系统、Java Card API 实现、预加载小程序的字节码(包括常量)等。它只能在现场读取,因此没有任何影响对于所问的问题。