Memory managment¶
Python and Javascript are interpreted languages and have a garbage collector. So the runtime takes care of the memory. This has the advantage that the programmer does not need to think about memory. The disadvantage is that he cannot control it and thus you can get unpredictable stalls in your program performance, when the garbage collector (typicall with mark and sweep algorithm) kicks in.
This is why compiled languages c++ and c are used in operating systems such as linux (c) and windows (c++).
import ROOT
import fortranmagic
%load_ext fortranmagic
import os
import sys
import numpy as np
if sys.platform.startswith("win"):
# Depends of system, python builds, and compilers compatibility.
# See below.
f_config = "--fcompiler=gnu95 --compiler=mingw32"
else:
# For Unix, compilers are usually more compatible.
f_config = ""
# Disable only deprecated NumPy API warning without disable any APIs.
f_config += " --extra '-DNPY_NO_DEPRECATED_API=0'"
%fortran_config {f_config}
New default arguments for %fortran: --extra '-DNPY_NO_DEPRECATED_API=0'
c++¶
%%cpp
class C{
public:
C(){
cout<<"created\n";
}
virtual ~C(){
cout<<"destructed\n";
}
int a;
int b;
};
//main(){
C c; //lives on stack
{
C c;
}
//}
created created destructed
if things need to live longer than the scope you need to use the heap. We have used the heap already with make_shared. Here we used a shared smart pointer to take care of creating and deleting the objects in memory through counting how many variables are pointing to it.
Internally objects are created with new and delete which generates pointers. it is recommendable to use the smart pointers (more in the next section):
%%cpp -d
class Class{
public:
Class(){
cout<<"created\n";
}
virtual ~Class(){
cout<<"destructed\n";
}
int a;
int b;
};
Class * createClass(){
Class *c=new Class;
return c;
}
%%cpp
//void main(){
Class *d;
d=createClass();
delete d;
//}
created destructed
You can also create dynamic c type arrays.
%%cpp
int i=100;
int *a=new int[i];
delete[] a;
There are three important smart pointers:
- unique_ptr
- shared_ptr
- weak_ptr
We start with the unique_ptr that has unique ownership of the object:
%%cpp
#include<memory>
using namespace std;
{
unique_ptr<C> p=make_unique<C>();
unique_ptr<C> p2=std::move(p);
p2->a=10;
cout <<p2->a<<endl;
//cout <<p->a<<endl;//if you comment this out the program crashes.
}
created 10 destructed
Then there is the shared_ptr for shared owenersip. It uses reference count to decide if when the object should be freed.
%%cpp
#include<memory>
using namespace std;
{
shared_ptr<C> p=make_shared<C>();
shared_ptr<C> p2=p;
p2->a=10;
cout <<p2->a<<endl;
cout <<p->a<<endl;
}
created 10 10 destructed
you can convert from unique_ptr to shared_ptr but not the other direction. So unique pointer is the best return type from a library function if you have dynamic data.
%%cpp
#include<memory>
using namespace std;
{
std::unique_ptr<std::string> unique = std::make_unique<std::string>("test");
std::shared_ptr<std::string> shared = std::move(unique); //only this is allowed
//std::unique_ptr<std::string> unique2 = std::move(shared); //not allowed
}
One problem of shared_ptr that implements reference counting are circular references where objects A points to object B and vice versa. Then they are never deleted. In those cases we need a week pointer, that does not change reference count but can be expired as seen below:
%%cpp -d
#include <iostream>
#include <memory>
std::weak_ptr<int> gw;
void observe()
{
std::cout << "gw.use_count() == " << gw.use_count() << "; ";
// we have to make a copy of shared pointer before usage:
if (std::shared_ptr<int> spt = gw.lock())
std::cout << "*spt == " << *spt << '\n';
else
std::cout << "gw is expired\n";
}
%%cpp
//int main()
//{
{
auto sp = std::make_shared<int>(42);
gw = sp;
observe();
}
observe();
//}
gw.use_count() == 1; *spt == 42 gw.use_count() == 0; gw is expired
c¶
c does not have smart pointer. you need to use malloc and free to manage the memory.
%%cpp
int *a=(int*)malloc(sizeof(int)*100);
a[0]=1;
a[1]=2;
free(a);
Javascript¶
Memory is managed by mark and sweep garbage collector. It basically goes through all variables on marks the object. Then all that are not marked are deleted. You can mark objects to be deleted by calling:
variablename=null
%%js //the next line is only necessary in jupyter notebooks
element.setAttribute('style', 'white-space: pre;');console.log=function(text){element.textContent+=text+"\n"}
class Geometry{
constructor() {
if (this.constructor == Geometry) {
throw new Error("Abstract classes can't be instantiated.");
}
}
getArea(){
throw new Error("Method 'getArea()' must be implemented.");
}
getCircumference(){
throw new Error("Method 'getCircumference()' must be implemented.");
}
}
class Rectangle extends Geometry{
constructor(width, height){
super();
this.width=width
this.height=height
}
getArea(){
return this.width*this.height;
}
getCircumference(){
return 2*this.width+this.height;
}
}
let c=new Rectangle(1,2);
console.log(c)
c=null
console.log(c)
Python¶
Memory is managed by mark and sweep garbage collector. But you can mark objects to be deleted by calling:
del variablename
a=[10,20,30]
print(a)
del a
print(a)
[10, 20, 30]
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[14], line 4 2 print(a) 3 del a ----> 4 print(a) NameError: name 'a' is not defined
Fortran¶
array on stack
%%fortran
! program and subroutine exchanged due to jupyternotebook
! program main
subroutine main()
implicit none
real*8,dimension(1:3) :: a=(/ 10,20,30 /)
! Variables
! Body of fortranterst
print *,a
! read * ! this does not work in jupyter
! end program
end subroutine main
main()
Fortran¶
array on heap
%%fortran
! program and subroutine exchanged due to jupyternotebook
! program main
subroutine main()
implicit none
real*8,dimension(:),allocatable :: a
real*8,dimension(:,:),allocatable :: M
real*8::dotprodresult
allocate(a(1:3))
allocate(M(1:3,1:3))
a=(/1,2,3/)
M=transpose(reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(M)))
print *,matmul(M,a)
print *,dot_product(a,a)
! read * ! this does not work in jupyter
deallocate(a)
deallocate(M)
! end program
end subroutine main
main()