在下面,您看到一个我编写的程序,该程序在调用requestObjectDeletion()方法后可以查看不同字段的状态和内存分配:

public class ReqObjDel extends Applet {
    static byte[] buffer = new byte[2];
    static boolean isNull = false;

    private ReqObjDel() {
    }

    public static void install(byte bArray[], short bOffset, byte bLength)
            throws ISOException {
        new ReqObjDel().register();
    }

    public void process(APDU arg0) throws ISOException {
        if (selectingApplet()) {
            return;
        }

        if (buffer != null && (short) buffer.length == (short) 10) {
            return;
        }

        byte[] oldBuffer = buffer;
        buffer = new byte[10];

        JCSystem.requestObjectDeletion();

        if (oldBuffer == null)
            isNull = true;

        if (isNull) {
            ISOException.throwIt((short) 0x1111);
        } else
            ISOException.throwIt((short) 0x0000);
    }
}


据我所知,此方法回收“不可达”对象正在使用的内存。为了“不可到达”,对象既不能由静态字段也不能由对象字段指向。因此,在上述程序中调用requestObjectDeletion()会回收oldBuffer所引用的EEPROM部分(据我所知,oldBuffer既不是类字段也不是对象字段,对吗?)。在这种情况下,我希望oldBuffer == null以及JCRE必须返回0x1111。但是输出意外地是0x0000

OSC: opensc-tool -s 00a404000b0102030405060708090000 -s 00000000
Using reader with a card: ACS CCID USB Reader 0
Sending: 00 A4 04 00 0B 01 02 03 04 05 06 07 08 09 00 00
Received (SW1=0x90, SW2=0x00)
Sending: 00 00 00 00
Received (SW1=0x00, SW2=0x00)


Q1:我可以得出什么结论?


那部分内存没有回收?
内存的那部分被回收了,但是oldBuffer仍然是对它的引用吗?
还有什么吗


Q2:在调用此方法之前和之后,是否有任何方法可以获取可用内存大小? (即是否有任何方法返回可用内存的大小[未分配]?)



更新1:尝试JCSystem.getAvailableMemory()

基于@vojta答案,我更改了程序,使行byte[] oldBuffer = buffer;仅运行一次(使用名为isFirstInvocation的标志),并在两次连续的process()方法调用中返回可用内存:

public class ReqObjDel extends Applet {
    static byte[] buffer = new byte[10];
    static boolean isFirstInvocation = true;

    private ReqObjDel() {
    }

    public static void install(byte bArray[], short bOffset, byte bLength)
            throws ISOException {
        new ReqObjDel().register();
    }

    public void process(APDU arg0) throws ISOException {
        if (selectingApplet()) {
            return;
        }

        short availableMem1 = JCSystem
                .getAvailableMemory(JCSystem.MEMORY_TYPE_PERSISTENT);

        if (isFirstInvocation) {

            byte[] oldBuffer = buffer;

            buffer = new byte[10];

            JCSystem.requestObjectDeletion();

            firstInvocation = false;
        }

        short availableMem2 = JCSystem
                .getAvailableMemory(JCSystem.MEMORY_TYPE_PERSISTENT);

        short availableMemory = (short) (availableMem1 + availableMem2);
        ISOException.throwIt(availableMemory);

    }
}


这是输出:

OSC: osc -s 00a404000b0102030405060708090000 -s 00000000 -s 00000000
Using reader with a card: ACS CCID USB Reader 0
Sending: 00 A4 04 00 0B 01 02 03 04 05 06 07 08 09 00 00
Received (SW1=0x90, SW2=0x00)
Sending: 00 00 00 00
Received (SW1=0xFF, SW2=0xFE)
Sending: 00 00 00 00
Received (SW1=0xFF, SW2=0xFE)


由于两个调用都返回相等的值,因此我认为JCRE在调用requestObjectDeletion()之后立即回收该部分内存,对吗?

最佳答案

首先,根据我的个人经验制定一条规则:如果可能,请不要使用垃圾收集器。 GC非常慢,甚至可能很危险(请参阅Javacard - power loss during garbage collection)。

Q1:
如果确实需要使用GC,请阅读文档:


  小程序调用此方法以触发对象删除
  Java Card运行时环境的服务。如果Java Card运行时
  环境实现对象删除机制,请求是
  仅在此时登录。 Java Card运行时环境必须
  在下一次调用之前安排对象删除服务
  Applet.process()方法。


简而言之,JCSystem.requestObjectDeletion();没有立即生效。这就是为什么您的局部变量oldBuffer保持不变的原因。

Q2:要找出您的小程序有多少持久性内存,请使用:

JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_PERSISTENT)


对更新1的回答:JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_PERSISTENT)对于具有32767字节以上持久性存储的卡可能会造成混淆。此类卡通常提供自己的专有方式来查找可用内存。


  如果可用字节数大于32767,则此
  方法返回32767。

07-24 03:36