anandanand84 / technicalindicators

A javascript technical indicators written in typescript with pattern recognition right in the browser
MIT License
2.13k stars 550 forks source link

This library is a memory leak #264

Open episage opened 1 year ago

episage commented 1 year ago

When calling SMA or other indicators relying on LinkedList the LinkedList items never get garbage collected. This is due to the structure of LinkedList that always contains reference to the next and previous element.

In NodeJS you must remove reference to the unused items for them to be disposed by GC.

episage commented 1 year ago

Also, SMA relies on approximate, running algorithm. It is efficient but the results will differ when compared to other software.

quanxufeng commented 1 year ago

When calling SMA or other indicators relying on LinkedList the LinkedList items never get garbage collected. This is due to the structure of LinkedList that always contains reference to the next and previous element.

In NodeJS you must remove reference to the unused items for them to be disposed by GC.

so how can I solve this problem? could you please tell me How can I modify the code to fix it?

denernun commented 1 year ago

Fork the repository Fix code Pull Request

On Tue, Mar 28, 2023 at 8:45 AM quanxufeng @.***> wrote:

When calling SMA or other indicators relying on LinkedList the LinkedList items never get garbage collected. This is due to the structure of LinkedList that always contains reference to the next and previous element.

In NodeJS you must remove reference to the unused items for them to be disposed by GC.

so how can I solve this problem? could you please tell me How can I modify the code to fix it?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>

episage commented 1 year ago

@quanxufeng

To solve this problem, you can create a custom LinkedList class that has built-in garbage collection support. This class will automatically remove references to unused items, allowing them to be properly disposed of by the garbage collector. Here's an example implementation:

class GCLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  append(value) {
    const newNode = { value, prev: null, next: null };

    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      newNode.prev = this.tail;
      this.tail.next = newNode;
      this.tail = newNode;
    }

    this.length++;
    return this;
  }

  remove(value) {
    let currentNode = this.head;

    while (currentNode) {
      if (currentNode.value === value) {
        if (currentNode.prev) {
          currentNode.prev.next = currentNode.next;
        } else {
          this.head = currentNode.next;
        }

        if (currentNode.next) {
          currentNode.next.prev = currentNode.prev;
        } else {
          this.tail = currentNode.prev;
        }

        currentNode.prev = null;
        currentNode.next = null;

        this.length--;
        return currentNode.value;
      }

      currentNode = currentNode.next;
    }

    return null;
  }

  removeFirst() {
    if (!this.head) {
      return null;
    }

    const removedNode = this.head;
    this.head = removedNode.next;

    if (this.head) {
      this.head.prev = null;
    } else {
      this.tail = null;
    }

    removedNode.next = null;
    this.length--;

    return removedNode.value;
  }

  removeLast() {
    if (!this.tail) {
      return null;
    }

    const removedNode = this.tail;
    this.tail = removedNode.prev;

    if (this.tail) {
      this.tail.next = null;
    } else {
      this.head = null;
    }

    removedNode.prev = null;
    this.length--;

    return removedNode.value;
  }
}

Now, you can use this custom LinkedList class to store your SMA or other indicator values. Whenever you no longer need a value, call the remove or removeFirst method, depending on your use case, to remove it from the list. This will ensure that the garbage collector disposes of the unused items properly.

For example:

const smaList = new GCLinkedList();

// Add new SMA values
smaList.append(smaValue);

// Remove old SMA values when no longer needed
smaList.removeFirst();

By managing your LinkedList items this way, you can ensure that unused items are correctly garbage collected, preventing memory leaks and improving your application's performance.

quanxufeng commented 1 year ago

@quanxufeng

To solve this problem, you can create a custom LinkedList class that has built-in garbage collection support. This class will automatically remove references to unused items, allowing them to be properly disposed of by the garbage collector. Here's an example implementation:

class GCLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  append(value) {
    const newNode = { value, prev: null, next: null };

    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      newNode.prev = this.tail;
      this.tail.next = newNode;
      this.tail = newNode;
    }

    this.length++;
    return this;
  }

  remove(value) {
    let currentNode = this.head;

    while (currentNode) {
      if (currentNode.value === value) {
        if (currentNode.prev) {
          currentNode.prev.next = currentNode.next;
        } else {
          this.head = currentNode.next;
        }

        if (currentNode.next) {
          currentNode.next.prev = currentNode.prev;
        } else {
          this.tail = currentNode.prev;
        }

        currentNode.prev = null;
        currentNode.next = null;

        this.length--;
        return currentNode.value;
      }

      currentNode = currentNode.next;
    }

    return null;
  }

  removeFirst() {
    if (!this.head) {
      return null;
    }

    const removedNode = this.head;
    this.head = removedNode.next;

    if (this.head) {
      this.head.prev = null;
    } else {
      this.tail = null;
    }

    removedNode.next = null;
    this.length--;

    return removedNode.value;
  }

  removeLast() {
    if (!this.tail) {
      return null;
    }

    const removedNode = this.tail;
    this.tail = removedNode.prev;

    if (this.tail) {
      this.tail.next = null;
    } else {
      this.head = null;
    }

    removedNode.prev = null;
    this.length--;

    return removedNode.value;
  }
}

Now, you can use this custom LinkedList class to store your SMA or other indicator values. Whenever you no longer need a value, call the remove or removeFirst method, depending on your use case, to remove it from the list. This will ensure that the garbage collector disposes of the unused items properly.

For example:

const smaList = new GCLinkedList();

// Add new SMA values
smaList.append(smaValue);

// Remove old SMA values when no longer needed
smaList.removeFirst();

By managing your LinkedList items this way, you can ensure that unused items are correctly garbage collected, preventing memory leaks and improving your application's performance.

Hi, episage, thanks for your reply, is there a method or a way that I can directly remove values or empty the list? can I access to the reference of the values list in this library?

episage commented 1 year ago

Yes, you can add a method to the custom GCLinkedList class to clear the list and remove all references to the values. Here's the updated class with the new clear method:

class GCLinkedList {
  // ... (previous code)

  clear() {
    let currentNode = this.head;

    while (currentNode) {
      let nextNode = currentNode.next;
      currentNode.prev = null;
      currentNode.next = null;
      currentNode = nextNode;
    }

    this.head = null;
    this.tail = null;
    this.length = 0;
  }
}

This clear method iterates through the list, removing references to the prev and next nodes, effectively clearing the list. After calling this method, the garbage collector will be able to dispose of the unused items properly. To use the clear method, simply call it on your GCLinkedList instance:

const smaList = new GCLinkedList();

// Add new SMA values
smaList.append(smaValue);

// Clear the list and remove all references
smaList.clear();

Regarding direct access to the values list, the custom GCLinkedList class does not store the values in a separate list or array, as it uses a linked list structure with nodes referencing each other. However, you can create a method to retrieve all values as an array:

class GCLinkedList {
  // ... (previous code)

  getValues() {
    const values = [];
    let currentNode = this.head;

    while (currentNode) {
      values.push(currentNode.value);
      currentNode = currentNode.next;
    }

    return values;
  }
}

Now you can access the list values as an array:

const smaList = new GCLinkedList();

// Add new SMA values
smaList.append(smaValue);

// Get the values as an array
const valuesArray = smaList.getValues();
console.log(valuesArray);

This will output an array containing the values of the nodes in the list.