humble-video-noarch/src/main/java/io/humble/ferry/JNIMemoryAllocator.java
public byte[] malloc(int size)
  {
    byte[] retval = null;
    // first check the parachute
    JNIMemoryParachute.getParachute().packChute();
    try
    {
      if (SHOULD_RETRY_FAILED_ALLOCS)
      {
        int allocationAttempts = 0;
        int backoffTimeout = 10; // start at 10 milliseconds
        while (true)
        {
          try
          {
            // log.debug("attempting malloc of size: {}", size);
            retval = new byte[size];
            // log.debug("malloced block of size: {}", size);
            // we succeed, so break out
            break;
          }
          catch (final OutOfMemoryError e)
          {
            // try clearing our queue now and do it again. Why?
            // because the first failure may have allowed us to
            // catch a RefCounted no longer in use, and the second
            // attempt may have freed that memory.

            // do a JNI collect before the alloc
            ++allocationAttempts;
            if (allocationAttempts >= MAX_ALLOCATION_ATTEMPTS)
            {
              // try pulling our rip cord
              JNIMemoryParachute.getParachute().pullCord();
              // do one last "hope gc" to free our own memory
              JNIReference.getMgr().gcInternal();
              // and throw the error back to the native code
              throw e;
            }

            log.debug("retrying ({}) allocation of {} bytes",
                allocationAttempts, size);
            try
            {
              // give the finalizer a chance
              if (allocationAttempts <= 1)
              {
                // first just yield
                Thread.yield();
              }
              else
              {
                Thread.sleep(backoffTimeout);
                // and slowly get longer...
                backoffTimeout = (int) (backoffTimeout * FALLBACK_TIME_DECAY);
              }
            }
            catch (InterruptedException e1)
            {
              // reset the interruption so underlying
              // code can also interrupt
              Thread.currentThread().interrupt();
              // and throw the error condition
              throw e;
            }
            // do a JNI collect before the alloc
            JNIReference.getMgr().gcInternal();
          }
        }
      }
      else
      {
        retval = new byte[size];
      }
      addToBuffer(retval);
      retval[retval.length - 1] = 0;
      
//      log.debug("malloc: {}({}:{})", new Object[]
//      {
//          retval.hashCode(), retval.length, size
//      });
      
    }
    catch (Throwable t)
    {
      // do not let an exception leak out since we go back to native code.
      retval = null;
    }
    return retval;
  }
public byte[] malloc(int size)
  {
    byte[] retval = null;
    // first check the parachute
    JNIMemoryParachute.getParachute().packChute();
    try
    {
      if (SHOULD_RETRY_FAILED_ALLOCS)
      {
        int allocationAttempts = 0;
        int backoffTimeout = 10; // start at 10 milliseconds
        while (true)
        {
          try
          {
            // log.debug("attempting malloc of size: {}", size);
            retval = new byte[size];
            // log.debug("malloced block of size: {}", size);
            // we succeed, so break out
            break;
          }
          catch (final OutOfMemoryError e)
          {
            // try clearing our queue now and do it again. Why?
            // because the first failure may have allowed us to
            // catch a RefCounted no longer in use, and the second
            // attempt may have freed that memory.

            // do a JNI collect before the alloc
            ++allocationAttempts;
            if (allocationAttempts >= MAX_ALLOCATION_ATTEMPTS)
            {
              // try pulling our rip cord
              JNIMemoryParachute.getParachute().pullCord();
              // do one last "hope gc" to free our own memory
              JNIReference.getMgr().gcInternal();
              // and throw the error back to the native code
              throw e;
            }

            log.debug("retrying ({}) allocation of {} bytes",
                allocationAttempts, size);
            try
            {
              // give the finalizer a chance
              if (allocationAttempts <= 1)
              {
                // first just yield
                Thread.yield();
              }
              else
              {
                Thread.sleep(backoffTimeout);
                // and slowly get longer...
                backoffTimeout = (int) (backoffTimeout * FALLBACK_TIME_DECAY);
              }
            }
            catch (InterruptedException e1)
            {
              // reset the interruption so underlying
              // code can also interrupt
              Thread.currentThread().interrupt();
              // and throw the error condition
              throw e;
            }
            // do a JNI collect before the alloc
            JNIReference.getMgr().gcInternal();
          }
        }
      }
      else
      {
        retval = new byte[size];
      }
      addToBuffer(retval);
      retval[retval.length - 1] = 0;
      
//      log.debug("malloc: {}({}:{})", new Object[]
//      {
//          retval.hashCode(), retval.length, size
//      });
      
    }
    catch (Throwable t)
    {
      // do not let an exception leak out since we go back to native code.
      retval = null;
    }
    return retval;
  }
public byte [ ] malloc ( int size ) { byte [ ] retval = null ; // first check the parachute JNIMemoryParachute . getParachute ( ) . packChute ( ) ; try { if ( SHOULD_RETRY_FAILED_ALLOCS ) { int allocationAttempts = 0 ; int backoffTimeout = 10 ; // start at 10 milliseconds while ( true ) { try { // log.debug("attempting malloc of size: {}", size); retval = new byte [ size ] ; // log.debug("malloced block of size: {}", size); // we succeed, so break out break ; } catch ( final OutOfMemoryError e ) { // try clearing our queue now and do it again. Why? // because the first failure may have allowed us to // catch a RefCounted no longer in use, and the second // attempt may have freed that memory. // do a JNI collect before the alloc ++ allocationAttempts ; if ( allocationAttempts >= MAX_ALLOCATION_ATTEMPTS ) { // try pulling our rip cord JNIMemoryParachute . getParachute ( ) . pullCord ( ) ; // do one last "hope gc" to free our own memory JNIReference . getMgr ( ) . gcInternal ( ) ; // and throw the error back to the native code throw e ; } log . debug ( "retrying ({}) allocation of {} bytes" , allocationAttempts , size ) ; try { // give the finalizer a chance if ( allocationAttempts <= 1 ) { // first just yield Thread . yield ( ) ; } else { Thread . sleep ( backoffTimeout ) ; // and slowly get longer... backoffTimeout = ( int ) ( backoffTimeout * FALLBACK_TIME_DECAY ) ; } } catch ( InterruptedException e1 ) { // reset the interruption so underlying // code can also interrupt Thread . currentThread ( ) . interrupt ( ) ; // and throw the error condition throw e ; } // do a JNI collect before the alloc JNIReference . getMgr ( ) . gcInternal ( ) ; } } } else { retval = new byte [ size ] ; } addToBuffer ( retval ) ; retval [ retval . length - 1 ] = 0 ; //      log.debug("malloc: {}({}:{})", new Object[] //      { //          retval.hashCode(), retval.length, size //      }); } catch ( Throwable t ) { // do not let an exception leak out since we go back to native code. retval = null ; } return retval ; }
Internal Only.  Allocate a new block of bytes. Called from native code.
<p>
Will retry many times if it can't get memory, backing off
in timeouts to get there.
</p>
<p>
Callers must eventually call {@link #free(byte[])} when done
with the bytes or a leak will result.
</p>
@param size
# of bytes requested
@return An array of size or more bytes long, or null on failure.
Internal Only . Allocate a new block of bytes . Called from native code . <p > Will retry many times if it can t get memory backing off in timeouts to get there . < / p > <p > Callers must eventually call {