The memory assigned is not disproportional; you are creating 100,000 objects! As you can see, they take up roughly 34 megabytes of space:
>>> sys.getsizeof(Test())+sys.getsizeof(Test().__dict__)
344
>>> (sys.getsizeof(Test())+sys.getsizeof(Test().__dict__)) * 1000000 / 10**6
34.4 #megabytes
You can get a minor improvement with __slots__, but you will still need about 20MB of memory to store those 100,000 objects.
>>> sys.getsizeof(Test2())+sys.getsizeof(Test2().__slots__)
200
>>> sys.getsizeof(Test2())+sys.getsizeof(Test2().__slots__) * 1000000 / 10**6
20.0 #megabytes
(With credit to mensi's answer, sys.getsizeof is not taking into account references. You can autocomplete to see most of the attributes of an object.)
See SO answer: Usage of __slots__? http://docs.python.org/release/2.5.2/ref/slots.html
To use __slots__:
class Test2():
__slots__ = ['a','b','c','d','e']
def __init__(self):
...
Answer from ninjagecko on Stack OverflowHi Pyople!
Yesterday I learned about sys.getsizeof() function and try some code. More specifically:
lst = [i for i in range(1000000000)] # one mld numbers, creating for about a minute
When I use sys.getsizeof(lst), it returns: 8058558880. Which is correct. But when I look at my system resources in Linux Centos7 IPython (Python 3.4) I see: ipython Memory: 39592564 K Shared Mem: 5176 K - That's freaking 40GB.
I don't understand why, if a object is 8 GB in size, takes 40 KGB system memory. I tried it in list that had around 400 MB and system took 400 * 5 (approx) = 2 GB (approx)
Why is it taking 5-times more memory than it should? Or is the problem only because I tried it in iPython / Konsole? And in program it wouldn't be a problem?
sys.getsizeof gives you the amount of memory allocated to the list itself, but you also have 10...00 int objects that the list only contains a pointer to.
The size of an object does not include the size of all the objects that that object refers to. For example:
>>> import sys
>>> foo = ['a' * 1000000]
>>> sys.getsizeof(foo)
40
>>> sys.getsizeof(foo[0])
1000025
foo is a list object that contains one item. Its size is 40 bytes, because that's how much memory it takes to store a list big enough to hold a reference to one object. That object happens to be about a megabyte in size, but it's a completely separate object from the list object and doesn't count towards the size of the list object.
The memory assigned is not disproportional; you are creating 100,000 objects! As you can see, they take up roughly 34 megabytes of space:
>>> sys.getsizeof(Test())+sys.getsizeof(Test().__dict__)
344
>>> (sys.getsizeof(Test())+sys.getsizeof(Test().__dict__)) * 1000000 / 10**6
34.4 #megabytes
You can get a minor improvement with __slots__, but you will still need about 20MB of memory to store those 100,000 objects.
>>> sys.getsizeof(Test2())+sys.getsizeof(Test2().__slots__)
200
>>> sys.getsizeof(Test2())+sys.getsizeof(Test2().__slots__) * 1000000 / 10**6
20.0 #megabytes
(With credit to mensi's answer, sys.getsizeof is not taking into account references. You can autocomplete to see most of the attributes of an object.)
See SO answer: Usage of __slots__? http://docs.python.org/release/2.5.2/ref/slots.html
To use __slots__:
class Test2():
__slots__ = ['a','b','c','d','e']
def __init__(self):
...
Every instance references a dict for it's __dict__ which is 272 bytes on my machine for your example. Multiply that by 100'000.
What is the difference between len() and sys.getsizeof() methods in python? - Stack Overflow
BUG: Incorrect results from `sys.getsizeof()` for multi-dimensional arrays
python - sys.getsizeof() results don't quite correlate to structure size - Stack Overflow
Why does a 9 GB list appear to use 40 GB of memory?
They are not the same thing at all.
len() queries for the number of items contained in a container. For a string that's the number of characters:
Return the length (the number of items) of an object. The argument may be a sequence (string, tuple or list) or a mapping (dictionary).
sys.getsizeof() on the other hand returns the memory size of the object:
Return the size of an object in bytes. The object can be any type of object. All built-in objects will return correct results, but this does not have to hold true for third-party extensions as it is implementation specific.
Python string objects are not simple sequences of characters, 1 byte per character.
Specifically, the sys.getsizeof() function includes the garbage collector overhead if any:
getsizeof()calls the object’s__sizeof__method and adds an additional garbage collector overhead if the object is managed by the garbage collector.
String objects do not need to be tracked (they cannot create circular references), but string objects do need more memory than just the bytes per character. In Python 2, __sizeof__ method returns (in C code):
Py_ssize_t res;
res = PyStringObject_SIZE + PyString_GET_SIZE(v) * Py_TYPE(v)->tp_itemsize;
return PyInt_FromSsize_t(res);
where PyStringObject_SIZE is the C struct header size for the type, PyString_GET_SIZE basically is the same as len() and Py_TYPE(v)->tp_itemsize is the per-character size. In Python 2.7, for byte strings, the size per character is 1, but it's PyStringObject_SIZE that is confusing you; on my Mac that size is 37 bytes:
>>> sys.getsizeof('')
37
For unicode strings the per-character size goes up to 2 or 4 (depending on compilation options). On Python 3.3 and newer, Unicode strings take up between 1 and 4 bytes per character, depending on the contents of the string.
For containers such as dictionaries or lists that reference other objects, the memory size given covers only the memory used by the container and the pointer values used to reference those other objects. There is no straightforward method of including the memory size of the ‘contained’ objects because those same objects could have many more references elsewhere and are not necessarily owned by a single container.
The documentation states it like this:
Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to.
If you need to calculate the memory footprint of a container and anything referenced by that container you’ll have to use some method of traversing to those contained objects and get their size; the documentation points to a recursive recipe.
key difference is that len() will give actual length of elements in container , Whereas sys.getsizeof() will give it's memory size which it occupy
for more information read docs of python which is available at https://docs.python.org/3/library/sys.html#module-sys
Can someone help me understand what's going on here? My OS reports that a python process whose only large object is a 9 GB list is consuming 40.6 GB of system memory. I repeated this test several times with both the interactive and standard interpreters and the results are pretty consistent.
import sys
import psutil
#memory in use prior to generating list
prior_used = psutil.virtual_memory().used
print(f"Prior used: {round(prior_used/1e9, 2)} GB")
z = [*range(1000000000)]
list_size = sys.getsizeof(z)
print(f"List size: {round(list_size/1e9, 2)} GB")
#memory in use after to generating list
post_used = psutil.virtual_memory().used
print(f"Post used: {round(post_used/1e9, 2)} GB")
difference = post_used - prior_used
print(f"Memory used by list: {round(difference/1e9, 2)} GB")
#clear the list
z = None
after_deleting = psutil.virtual_memory().used
print(f"Memory used after clearing the list: {round(after_deleting/1e9, 2)} GB")
# output:
# Prior used: 3.27 GB
# List size: 9.0 GB
# Post used: 43.87 GB
# Memory used by list: 40.6 GB
# Memory used after clearing the list: 3.28 GBI will attempt to answer your question from a broader point of view. You're referring to two functions and comparing their outputs. Let's take a look at their documentation first:
- len():
Return the length (the number of items) of an object. The argument may be a sequence (such as a string, bytes, tuple, list, or range) or a collection (such as a dictionary, set, or frozen set).
So in case of string, you can expect len() to return the number of characters.
- sys.getsizeof():
Return the size of an object in bytes. The object can be any type of object. All built-in objects will return correct results, but this does not have to hold true for third-party extensions as it is implementation specific.
So in case of string (as with many other objects) you can expect sys.getsizeof() the size of the object in bytes. There is no reason to think that it should be the same as the number of characters.
Let's have a look at some examples:
>>> first = "First"
>>> len(first)
5
>>> sys.getsizeof(first)
42
This example confirms that the size is not the same as the number of characters.
>>> second = "Second"
>>> len(second)
6
>>> sys.getsizeof(second)
43
We can notice that if we look at a string one character longer, its size is one byte bigger as well. We don't know if it's a coincidence or not though.
>>> together = first + second
>>> print(together)
FirstSecond
>>> len(together)
11
If we concatenate the two strings, their combined length is equal to the sum of their lengths, which makes sense.
>>> sys.getsizeof(together)
48
Contrary to what someone might expect though, the size of the combined string is not equal to the sum of their individual sizes. But it still seems to be the length plus something. In particular, something worth 37 bytes. Now you need to realize that it's 37 bytes in this particular case, using this particular Python implementation etc. You should not rely on that at all. Still, we can take a look why it's 37 bytes what they are (approximately) used for.
String objects are in CPython (probably the most widely used implementation of Python) implemented as PyStringObject. This is the C source code (I use the 2.7.9 version):
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
*/
} PyStringObject;
You can see that there is something called PyObject_VAR_HEAD, one int, one long and a char array. The char array will always contain one more character to store the '\0' at the end of the string. This, along with the int, long and PyObject_VAR_HEAD take the additional 37 bytes. PyObject_VAR_HEAD is defined in another C source file and it refers to other implementation-specific stuff, you need to explore if you want to find out where exactly are the 37 bytes. Plus, the documentation mentions that sys.getsizeof()
adds an additional garbage collector overhead if the object is managed by the garbage collector.
Overall, you don't need to know what exactly takes the something (the 37 bytes here) but this answer should give you a certain idea why the numbers differ and where to find more information should you really need it.
To quote the documentation:
Return the size of an object in bytes. The object can be any type of object. All built-in objects will return correct results, but this does not have to hold true for third-party extensions as it is implementation specific.
Built in strings are not simple character sequences - they are full fledged objects, with garbage collection overhead, which probably explains the size discrepancy you're noticing.
It's not clear from your question whether you want to the compressed or uncompressed size of the file, but in the former case, it's easy with the os.path.getsize function from the os module
>>> import os
>>> os.path.getsize('flickrapi-1.2.tar.gz')
35382L
To get the answer in megabytes you can shift the answer right by 20, e.g.
os.path.getsize('large.tar.gz') >> 20
Although that operation will be done in integers - if you want to preserve fractions of a megabyte, divide by (1024*1024.0) instead. (Note the .0 so that the divisor will be a float.)
Update: In the comments below, Johnsyweb points out a useful recipe for more generally producing human readable representations of file sizes.
To get file size in MB, I created this function:
import os
def get_size(path):
size = os.path.getsize(path)
if size < 1024:
return f"{size} bytes"
elif size < pow(1024,2):
return f"{round(size/1024, 2)} KB"
elif size < pow(1024,3):
return f"{round(size/(pow(1024,2)), 2)} MB"
elif size < pow(1024,4):
return f"{round(size/(pow(1024,3)), 2)} GB"
>>> get_size("k.tar.gz")
1.4MB