A few days ago, I went to Dongguan to read the e-book “In-depth Understanding of the Linux kernel” on the road, which talked about Linux virtual memory dynamic allocation technology, associated with the learning work eventually involved in some experiences, close-up this article. (Due to my limited level, there are inevitable mistakes, welcome to be corrected ~)
1. Lazy thinking
I think you must have more or less Deadline experience in your study and work. You leave your homework until the beginning of the semester and do your laundry until there is no clothes to change. Is that a good idea? In fact, it is ok. Generally, it is efficient to do homework and laundry during the Deadline. But to put it another way, before you do homework and laundry, you are free to do other things
2. Lazy thinking in Linux dynamic memory allocation
2.1 Request page Relocation
The term “request paging” refers to postponing the allocation of page frames (virtual memory scheduling units) until it can no longer be postponed, that is, until the page the process is accessing is no longer in RAM, thus causing a page miss exception.
The motivation behind the request paging technique is that a process does not start running with access to all addresses in its address space; In fact, some addresses may never be used by the process. In addition, the principle of locality of a program ensures that only a small number of process pages are actually referenced at each stage of program execution, so that the page boxes that are temporarily unused can be used by other processes. This increases the average number of free page boxes in the system, making better use of free memory. From another point of view, requesting paging gives the system overall greater throughput while the total amount of RAM remains constant.
2.2 Copy while writing (aka famouslyCopy On Write
)
The first generation of Unix systems implemented a kind of dumb process creation: when a child process is forked (), the kernel copies the entire address space of the parent process as-is and assigns that copy to the child process. The child process has many of the same things as the parent process and does not change, so there is no need to make a complete copy, which wastes a lot of memory space and consumes a lot of CPU cycles.
Modern Unix kernels (including Linux) use a more efficient method called copy-on-write, also known as Copy On Write. The idea is fairly simple: the parent and child process share the page frame, but they cannot change it. When they do, an exception occurs, and the kernel copies the page into a new page frame and marks it as writable, leaving the original page frame still write-protected. When other processes attempt to write, the kernel checks whether the writer is the sole owner of the page box, and if so, marks the page box as writable to the process.
The _count field of the page descriptor is used to track the number of processes that share the current page frame. If it is 1, it indicates that the page frame is shared with one other process; if it is 0, it indicates that only one process can write; if it is -1, it indicates that the page frame needs to be released.
3. Give some examples of languages
3.1 JS
Negative Language textbook
Nuggets on the front-end development of the students more, must be very understanding of the depth of JS copy questions, ask rotten interview questions
let a = {a:1};
let b = a;
let c = Object.assign({}, a)
console.log(a,b,c,a===b,a===c);
//{a:1} {a:1} {a:1} true false
a.a = 2;
console.log(a,b,c,a===b,a===c);
//{a:2} {a:2} {a:1} true false
Copy the code
The code above is easy to understand,b
It’s a shallow copy, so the value changes,c
Is a deep copy, so the value does not change and the corresponding variable memory address, becauseJS
You can’t explicitly view the memory address of a variable as in other languages, but you canChrome
theMemory
Tool snapshot to view its correspondingV8
Virtual address.
Obviously, A and B have the same memory address, while C, because it is a deep copy, has a new memory address. At Snapshot 12, the memory address of a and B is still the same. This is because JS does not implement copy-on-write.
3.2 Python
When writing copy
After the JS lesson, let’s look at a language that implements copy-on-write.
terence@k8s-master:/mydata$python3 Python 3.6.9 (default, Oct 8 2020, 12:12:24) [GCC 8.4.0] on Linux Type "help", "copyright", "credits" or "license" for more information. >>> a = "qwer" >>> print(a) qwer >>> id(a) 140662197387536 >>> b = a; >>> print(b) qwer >>> ID (b) 140662197387536 = "qWERt" >>> ID (b) 140662197387592Copy the code
3.3 PHP
When writing copy
PHP implements copy-on-write for arrays. Normal variables are not copy-on-write. In the following example, you can clearly see the change in memory usage by modifying $b[0] and copying the whole thing. (Of course, you can also install the XDEBUG extension to see the array refCount reference count change)
$a = [];
for ($i=0; $i < 10000; $i{+ +)$a[] = rand(0.10000);
}
var_dump(memory_get_usage());/ / 9438664
$b = $a;
var_dump(memory_get_usage());/ / 9438664
$b[0] = 99999;
var_dump(memory_get_usage());/ / 9967104
Copy the code
3.4 Rust
When writing copy
fn main() {
let s1 = String::from("hello");
println!("s1 heap address: {:p}", s1.as_ptr());//0x5645e759d9e0
println!("s1 stack address: {:p}", &s1);//0x7fff6a964bb8
let mut s2 = s1;
println!("s2 heap address: {:p}", s2.as_ptr());//0x5645e759d9e0
println!("s2 stack address: {:p}", &s2);//0x7fff6a964c70
s2 = String::from("world");
println!("s2 heap address: {:p}", s2.as_ptr());//0x5645e759da70
println!("s2 stack address: {:p}", &s2);//0x7fff6a964c70
}
Copy the code
With a system-level language like Rust, we can more intuitively see how local variables in the stack relate to the actual memory location in the heap.
To see what happens at the bottom of String. A String consists of three parts, as shown on the left: a pointer to the memory that holds the String contents, a length, and a capacity. This set of data is stored on the stack. On the right is the memory portion of the heap where the contents are stored.
The length indicates how many bytes of memory are currently used for the contents of the String. Capacity is the total number of bytes of memory that String retrieves from the operating system. The difference between length and capacity is important, but not in the current context, so you can ignore capacity for now.
When we assign S1 to S2, the String’s data is copied, which means we copy its pointer, length, and capacity off the stack. We are not copying the heap to which the pointer points. In other words, the data in memory is represented as shown below
When we modify S2, we copy it while writing, and it becomes something like this
4 Other Examples
There are lazy loading, throttling and shaking prevention in the front end development scenario, which are actually the embodiment of inertia, not to allocate resources until absolutely necessary.