一种利用JNI实现的对象序列化存储库,使得JAVA序列化对象后可以存于内存区高速读写,并且不受JVM控制以避免OOM

33 篇文章 0 订阅
24 篇文章 0 订阅

      本工程应用于一款安卓白板程序,因有读写速度需求,传统的序列化到磁盘的方式读写速度太慢,因此想到了利用C的特性写一个高速缓存,并继承实现了自己的inputStream和outputStream,用于暂存大规模多叉树和大体积对象。本工程的序列化对象的数据均使用一个整数作为标记进行区分,有需要的朋友可以修改本工程的实现以支持字符串标记。

      本工程的JNI部分:

      一、首先是MakeFile:

     

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_LDLIBS    := -lm -llog
LOCAL_MODULE    := JNIArrayMemmapUtil
LOCAL_SRC_FILES := ArrayMemmapUtil.c
include $(BUILD_SHARED_LIBRARY)


    二、本工程的C工程文件  ArrayMemmapUtil.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include <string.h>
#include<jni.h>
#include<android/log.h>


#include <stdint.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>


#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))


#define new(Class) (Class*)malloc(sizeof(Class))  


//行单元节点 
typedef struct Node Node;
struct Node {
	jbyte content;
	Node *next;
};


typedef struct NodeHead NodeHead;
struct NodeHead {
	jbyte content;
	Node *next;
	Node *last;
};


//列单元结点 
typedef struct NodeList NodeList;
struct NodeList {
	NodeHead* nodeHead;
	NodeList* next;
	jint tag;
	jint size;
};


NodeList* nodeList = NULL;


/**用于创建序列化了的对象的jint数组
tag 列标记
dataItem 保存的数据**/
void Java_com_cjz_image_ArrayHeap_outputData (JNIEnv *env, jobject obj, jint tag, jint dataItem) {   
	NodeList* listCursor = NULL;   //找表游标 
	static NodeList* targetList = NULL;   //利用static实现表缓存,减少读写同一个表时的读写次数 
	if(nodeList == NULL){   //还没有列,创建一个,而且列标记就用传入的 
		nodeList = new(NodeList);
		nodeList->tag = tag;
		nodeList->nodeHead = NULL;
		nodeList->next = NULL;
		nodeList->size = 0;
	}
	if(targetList == NULL || targetList->tag != tag) { //如果已经缓存了正确的表就不遍历 
		//查找nodeList中有没有对应tag的数据表 
		listCursor = nodeList;
		while(listCursor != NULL){
			if(listCursor->tag == tag){
				targetList = listCursor;
				break;
			}
			listCursor = listCursor->next;
		}
		//如果找不到,就在表中新建一个
		if(targetList == NULL || targetList->tag != tag) {
			listCursor = nodeList;
			while(listCursor->next != NULL){
				//遍历到最后一个有效结点 
				listCursor = listCursor->next;
			}
			listCursor->next = new(NodeList);
			listCursor = listCursor->next;
			listCursor->tag = tag;
			listCursor->nodeHead = NULL;
			listCursor->next = NULL;
			listCursor->size = 0;
			targetList = listCursor;
		} 


	}
	//写入行数据 
	if(targetList->nodeHead == NULL) {
		targetList->nodeHead = new(NodeHead);
		targetList->nodeHead->next = NULL;
		targetList->nodeHead->last = (Node*) targetList->nodeHead;
	}
	targetList->nodeHead->last->content = (jbyte) (dataItem & 0xFF);
//	LOGI("input data:%d\n", targetList->nodeHead->last->content);
	targetList->nodeHead->last->next = new(Node);
	targetList->nodeHead->last->next->next = NULL;
	targetList->nodeHead->last = targetList->nodeHead->last->next;
	targetList->size++; 
}


jint Java_com_cjz_image_ArrayHeap_read(JNIEnv *env, jobject obj, jint tag) {
	NodeList* listCursor = NULL;
	static NodeList* targetList = NULL;
	if(targetList == NULL || targetList->tag != tag){ //如果已经缓存了正确的表就不遍历 
		//查找nodeList中有没有对应tag的数据表 
		listCursor = nodeList;
		while(listCursor != NULL){
			if(listCursor->tag == tag){
				targetList = listCursor;
				break;
			}
			listCursor = listCursor->next;
		}
	}
	if(targetList == NULL){
		return -1;
	}
		
	if(targetList->nodeHead == NULL)
		return -1;
	if(targetList->nodeHead->next == NULL){
		free(targetList->nodeHead);
		targetList->nodeHead = NULL;
		return -1;
	}
	jbyte data = targetList->nodeHead->content;
	NodeHead* beDelNode = targetList->nodeHead;
	if(targetList->nodeHead->next != NULL){
		NodeHead* newNodeHead = new(NodeHead);
		memcpy(newNodeHead, targetList->nodeHead->next, sizeof(NodeHead));
		newNodeHead->last = beDelNode->last;
		free(targetList->nodeHead->next);
		targetList->nodeHead = newNodeHead;
	}	
	else
		targetList->nodeHead = NULL;
	free(beDelNode); 
	targetList->size--;
	return (data & 0xFF);
}


/**读指定位置数据**/
jint Java_com_cjz_image_ArrayHeap_readPos(JNIEnv *env, jobject obj, jint tag, jint pos) {
	NodeList* listCursor = NULL;
	static NodeList* targetList = NULL;
	static jint lastPos = 0;
	static Node* lastNode = NULL;
	Node* cursor = NULL;
	jint i;
	
	if(targetList == NULL || targetList->tag != tag){ //如果已经缓存了正确的表就不遍历 
		//查找nodeList中有没有对应tag的数据表 
		listCursor = nodeList;
		while(listCursor != NULL){
			if(listCursor->tag == tag){
				targetList = listCursor;
				break;
			}
			listCursor = listCursor->next;
		}
	}
	if(targetList == NULL){
		return -1;
	}
	if(targetList->nodeHead == NULL)
		return -1;
	//找目标地址的数据:	
	//如果地址不在附近就重新遍历 
	if(lastPos == 0 || lastNode == NULL || pos - lastPos < 0) {
		cursor = (Node*) targetList->nodeHead;
		lastPos = pos;
		while(pos-- > 0) {
			if(cursor->next != NULL)
				cursor = cursor->next;
			else
				return -1;
		}
		lastNode = cursor;
		return (lastNode->content & 0xFF);
	} else { //否则后面附近的话就用上次记录的位置偏移过去,节约时间 
		jint offset = pos - lastPos;
		lastPos += offset;
		cursor = lastNode;
		while(offset-- > 0) {
			if(cursor->next != NULL)
				cursor = cursor->next;
			else
				return -1;
		}
		lastNode = cursor;
		return (lastNode->content & 0xFF);
	}
}


void clearProc(Node* head) {
	Node* cursor = head;
	while(cursor != NULL) {
		Node* next = cursor->next;
		free(cursor);
		cursor = next;
	}
}




jint Java_com_cjz_image_ArrayHeap_getSize(JNIEnv *env, jobject obj, jint tag){
	NodeList* listCursor = nodeList;
	while(listCursor != NULL){
		if(listCursor->tag == tag){
			return listCursor->size;
		}
		listCursor = listCursor->next;
	}
	return -1;
}


void Java_com_cjz_image_ArrayHeap_clear(JNIEnv *env, jobject obj, jint tag) {
	NodeList* listCursor = nodeList;
	while(listCursor != NULL){
		if(listCursor->tag == tag){
			Node* head = (Node*) listCursor->nodeHead;
			if(head != NULL){
				clearProc(head);	
				listCursor->nodeHead = NULL; 
				listCursor->size = 0;
			}
			return;	
		}
		listCursor = listCursor->next;
	}
}

 三、数据结构关系:




      使用ndk-build工具编译后,生成.so文件,复制到目标工程的app\libs文件夹中,然后app的build.gradle中加入:


sourceSets参数


在Android Stuido中添加包:com.cjz.image。

并粘贴一个jni十字链表使用类ArrayHeap,和如下inputStream和outputStream实现即可使用本功能 :


ArrayHeap:

      

package com.cjz.image;

import android.util.Log;

import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by cjz on 2018/6/25.
 */

public class ArrayHeap {
    static {
        System.loadLibrary("JNIArrayMemmapUtil");
    }

//    public static Map<Integer, Queue<Integer>> fakeDatas = new HashMap<>();

    private static native void outputData(int tag, int dataItem);

    private static native int read(int tag);

    private static native int readPos(int tag, int pos);

    public static native void clear(int tag);

    public static native int getSize(int tag);

    public static void output(int tag, int dataItem){
//        Log.i("output", "tag:" + tag + ", dataItem:" + dataItem);
        outputData(tag, dataItem);
//        if(fakeDatas.get(tag) == null){
//            fakeDatas.put(tag, new LinkedBlockingQueue<Integer>());
//            fakeDatas.get(tag).add(dataItem);
//        } else {
//            fakeDatas.get(tag).add(dataItem);
//        }
    }

    public static int readData(int tag){
        return read(tag);
//        return fakeDatas.get(tag).poll();
    }

    public static int readData(int tag, int position) {
        return readPos(tag, position);
    }

}


ArrayOutputStream类:

package com.cjz.image;

import android.util.Log;

import java.io.IOException;
import java.io.OutputStream;

/**
 * Created by cjz on 2018/6/25.
 */

public class ArrayOutputStream extends OutputStream {

    private final int tag;

    public ArrayOutputStream(int tag){
        this.tag = tag;
    }

    @Override
    public void write(int b) throws IOException {
//        Log.i("ArrayOutputStream", "write(int b)");
        ArrayHeap.output(tag, b);
    }

}

ArrayInputStream类:

package com.cjz.image;

import android.util.Log;

import java.io.IOException;
import java.io.InputStream;


public
class ArrayInputStream extends InputStream {

    private int tag;
//    protected byte buf[];

    protected int pos;

    protected int mark = 0;

    protected int count;

    public ArrayInputStream(int tag) {
        this.tag = tag;
        Log.i("ArrayHeap.getSize(tag)", ArrayHeap.getSize(tag) + "");
        this.pos = 0;
        this.count = ArrayHeap.getSize(tag);
    }

    public ArrayInputStream(byte buf[], int offset, int length) {
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.mark = offset;
    }

    public synchronized int read() {
        return (pos < count) ? (ArrayHeap.readData(tag, pos++) & 0xff) : -1;
    }


    public synchronized int read(byte b[], int off, int len) {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        }

        if (pos >= count) {
            return -1;
        }

        int avail = count - pos;
        if (len > avail) {
            len = avail;
        }
        if (len <= 0) {
            return 0;
        }
//        System.arraycopy(buf, pos, b, off, len);
        for(int i = 0; i < len; i++){
            b[off + i] = (byte) (ArrayHeap.readData(tag, pos++) & 0xff);
        }
//        pos += len;
        return len;
    }


    public synchronized long skip(long n) {
        Log.i("ArrayInputStream", "skip(long n)");
        long k = count - pos;
        if (n < k) {
            k = n < 0 ? 0 : n;
        }

        pos += k;
        return k;
    }

    public synchronized int available() {
        Log.i("ArrayInputStream", "available");
        return count - pos;
    }

    public boolean markSupported() {
        Log.i("ArrayInputStream", "markSupported");
        return true;
    }


    public void mark(int readAheadLimit) {
        Log.i("ArrayInputStream", "readAheadLimit");
        mark = pos;
    }

    public synchronized void reset() {
        Log.i("ArrayInputStream", "reset");
        pos = mark;
    }

    public void close() throws IOException {
        ArrayHeap.clear(tag);
    }

}


至此,本工程导入即可完成。


使用例子代码片段:

导入一个大对象到JNI管控的内存区中:



保存之前的内存占用量:

保存之后的内存占用量:


再从中读出对象:


可以明显地看到使用JNI保存序列化对象或者大块的数据除了可以提供高速读写的能力之外,还可以规避JVM OOM的危险。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值