nd4j-backends/nd4j-backend-impls/nd4j-cuda/src/main/java/org/nd4j/jita/memory/impl/CudaFullCachingProvider.java
@Override
    public void free(AllocationPoint point) {
        if (point.getAllocationStatus() == AllocationStatus.DEVICE) {
            if (point.isConstant())
                return;

            AllocationShape shape = point.getShape();
            int deviceId = point.getDeviceId();
            long address = point.getDevicePointer().address();
            long reqMemory = AllocationUtils.getRequiredMemory(shape);
            // we don't cache too big objects

            if (reqMemory > CudaEnvironment.getInstance().getConfiguration().getMaximumDeviceCacheableLength() || deviceCachedAmount.get(deviceId).get() >= CudaEnvironment.getInstance().getConfiguration().getMaximumHostCache()) {
                //log.info("DEVICE_{} memory purging: {} bytes; MS: {}; MT: {}", deviceId, reqMemory, MAX_GPU_ALLOCATION, MAX_GPU_CACHE);
                super.free(point);
                return;
            }

//            log.info("Saving HOST memory into cache...");

            ensureDeviceCacheHolder(deviceId, shape);

            CacheHolder cache = deviceCache.get(deviceId).get(shape);



            if (point.getDeviceId() != deviceId)
                throw new RuntimeException("deviceId changed!");

            // memory chunks < threshold will be cached no matter what
            if (reqMemory <= FORCED_CACHE_THRESHOLD) {
                cache.put(new CudaPointer(point.getDevicePointer().address()));
                return;
            } else {
                long cacheEntries = cache.size();
                long cacheHeight = deviceCache.get(deviceId).size();

                // total memory allocated within this bucket
                long cacheDepth = cacheEntries * reqMemory;

                //if (cacheDepth < MAX_CACHED_MEMORY / cacheHeight) {
                cache.put(new CudaPointer(point.getDevicePointer().address()));
                return;
                //} else {
                //    super.free(point);
                // }
            }
        }
        super.free(point);
    }
@Override
    public void free(AllocationPoint point) {
        if (point.getAllocationStatus() == AllocationStatus.DEVICE) {
            if (point.isConstant())
                return;

            AllocationShape shape = point.getShape();
            int deviceId = point.getDeviceId();
            long address = point.getDevicePointer().address();
            long reqMemory = AllocationUtils.getRequiredMemory(shape);
            // we don't cache too big objects

            if (reqMemory > CudaEnvironment.getInstance().getConfiguration().getMaximumDeviceCacheableLength() || deviceCachedAmount.get(deviceId).get() >= CudaEnvironment.getInstance().getConfiguration().getMaximumHostCache()) {
                //log.info("DEVICE_{} memory purging: {} bytes; MS: {}; MT: {}", deviceId, reqMemory, MAX_GPU_ALLOCATION, MAX_GPU_CACHE);
                super.free(point);
                return;
            }

//            log.info("Saving HOST memory into cache...");

            ensureDeviceCacheHolder(deviceId, shape);

            CacheHolder cache = deviceCache.get(deviceId).get(shape);



            if (point.getDeviceId() != deviceId)
                throw new RuntimeException("deviceId changed!");

            // memory chunks < threshold will be cached no matter what
            if (reqMemory <= FORCED_CACHE_THRESHOLD) {
                cache.put(new CudaPointer(point.getDevicePointer().address()));
                return;
            } else {
                long cacheEntries = cache.size();
                long cacheHeight = deviceCache.get(deviceId).size();

                // total memory allocated within this bucket
                long cacheDepth = cacheEntries * reqMemory;

                //if (cacheDepth < MAX_CACHED_MEMORY / cacheHeight) {
                cache.put(new CudaPointer(point.getDevicePointer().address()));
                return;
                //} else {
                //    super.free(point);
                // }
            }
        }
        super.free(point);
    }
This method frees specific chunk of memory, described by AllocationPoint passed in

PLEASE NOTE: This method can actually ignore free, and keep released memory chunk for future reuse.

@param point