jna

Java JNA: call DLL, run it several times, and then error "Invalid memory access"


The DLL is created by Java, calling Delphi through JNA, and can be run several times before an error will be reported. Is it because of the wrong type, or because there is a problem with the internal memory of the DLL or something?

Java signature:

public interface DLL extends Library {

    DLL CCAWDll = Native.load("invoke", DLL.class);

    String invoke(String fileName, float a, float b, float c, float d,
                 float e, float f, float g, float h, float i,
                 float j, String k);

}

Delphi signature:

function invoke(filename: PAnsiChar; a, b, c, d, e, f, g, h, i, j: single; k: PAnsiChar): PAnsiChar; stdcall;

Error stack:

===================================9th time, call DLL===================================
===================================10th time, call DLLL===================================
2023-03-10 10:25:56.417 ERROR 12592 --- [-nio-94-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : > Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.Error: Invalid memory access] with root cause

java.lang.Error: Invalid memory access
  at com.sun.jna.Native.invokePointer(Native Method) ~[jna-5.5.0.jar:5.5.0 (b0)]
  at com.sun.jna.Function.invokePointer(Function.java:497) ~[jna-5.5.0.jar:5.5.0 (b0)]
  at com.sun.jna.Function.invokeString(Function.java:660) ~[jna-5.5.0.jar:5.5.0 (b0)]
  at com.sun.jna.Function.invoke(Function.java:434) ~[jna-5.5.0.jar:5.5.0 (b0)]
  at com.sun.jna.Function.invoke(Function.java:361) ~[jna-5.5.0.jar:5.5.0 (b0)]
  at com.sun.jna.Library$Handler.invoke(Library.java:265) ~[jna-5.5.0.jar:5.5.0 (b0)]
  at com.sun.proxy.$Proxy98.invoke(Unknown Source) ~[na:na]
  at com.xxx.xxx.dll.impl.DllImpl.invoke(DllImpl.java:40) ~[classes/:na]
  at com.xxx.xxx.dll.impl.DllImpl$$FastClassBySpringCGLIB$$92dce636.invoke(<generated>) ~[classes/:na]
  at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.12.RELEASE.jar:5.1.12.RELEASE]
  at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:750) ~[spring-aop-5.1.12.RELEASE.jar:5.1.12.RELEASE]
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.12.RELEASE.jar:5.1.12.RELEASE]
  at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:120) ~[spring-context-5.1.12.RELEASE.jar:5.1.12.RELEASE]
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.12.RELEASE.jar:5.1.12.RELEASE]
  at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) ~[spring-aop-5.1.12.RELEASE.jar:5.1.12.RELEASE]
  at com.xxx.xxx.dll.impl.DllImpl$$EnhancerBySpringCGLIB$$c67e343.invoke(<generated>) ~[classes/:na]
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_341]
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_341]
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_341]
  at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_341]

No exception was found from the memory in the JVM and no overflow was seen. I don't know if there is a problem with the data I checked.

Method call:

public  class DllImpl extends DllManage implements DllProvider {

    public static AtomicInteger count = new AtomicInteger(0);
    @Override
    public synchronized ResponseEntity<String> invoke(String fileName, float a, float b,
                                                      float c, float d, float e, float f, float g, float h, float i, float j, String k) {
        System.setProperty("jna.encoding", "GBK");
        count.getAndIncrement();
        String result = null;
        System.out.println("==================================="+count+"th time, call DLL===================================");
        try {
            result = DLL.CCAWDll.invoke(fileName,
                    a,b,c,d,e,f,g,h,i,j,
                    k);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        System.gc();
        return new ResponseEntity<>(result, HttpStatus.OK);

    }
}

Delphi Specific code:

QueryPerformanceCounter(t2); // Get end count value
r1 := (t2 - t1) / c1; // Get the timing time in seconds (s)
r2 := (t2 - t1) / c1 * 1000; // Get the timing time in milliseconds (ms)
r3 := (t2 - t1) / c1 * 1000000; // Get the timing time in microseconds
WritelnTxt('fileName:' + filename);
WritelnTxt('Time of calling CCAWDll:' + FloatToStr(r2));
//TJsonSerializer class, more efficient and faster
serial := TJsonSerializer.Create;
S := serial.Serialize<INSOLUTION>(returnDetail); // [{"name": "Zhang Zhang", "age": 1}, {"name": "Wang Wang", "age": 2}]
QueryPerformanceCounter(t3); // Get end count value
r4 := (t3 - t2) / c1 * 1000; // Get timing time
WritelnTxt('Generate json time:' + FloatToStr(r4));
//write file
WritelnTxtFile(S);
//return 
GetMem(PStr, Length(S) + 1); // This 1 is used to store the 0 string terminator
StrPCopy(PStr, S);
Result := PAnsiChar(PStr);

returnDetail is the return data. I convert it to json and send it to java.

If I write directly to the file, no error will be reported. If I convert it to PansiCHar and return it to java, an error will be reported after multiple calls.


Solution

  • delphi:--> PStr :

    GetMem(PStr, Length(S) + 1);   
    

    The applied memory has not been released. The new code: PStr Use global variables.

    if(PStr <> nil) then FreeMem(PStr);
    PStr := GetMemory(Length(S) + 1);