Skip to main content

Command Palette

Search for a command to run...

Syllabus software engineering (7)

Updated
54 min readView as Markdown

Giai đoạn 14 - Runtime Internals (Ngày 764–903)

Giai đoạn dài thứ 2 trong roadmap. Đi xuống tầng sâu nhất: bên trong JVM, CPython, và ABI của C++ - nơi mọi framework đã học ở Giai đoạn 10 thực sự chạy.

Nhóm 1: Giới thiệu (Ngày 764–765)

Ngày 764: Giới thiệu Runtime Internals

  • Mục tiêu: Hiểu vì sao đây là tầng sâu nhất và cuối cùng cần học trước khi đọc source code thật (Giai đoạn 15).

  • Lý thuyết: Mọi framework (Spring, CPython libraries, C++ libraries) đều chạy trên 1 runtime cụ thể - hiểu runtime giúp giải thích được những hành vi "kỳ lạ" mà framework không tự giải thích được (VD: vì sao warm-up JVM lại quan trọng, vì sao Python thread không tăng tốc CPU-bound).

  • Thực hành: Liệt kê 3 câu hỏi về hiệu năng bạn từng thắc mắc mà có thể chỉ trả lời được sau khi hiểu runtime (VD: "tại sao benchmark chạy lần đầu luôn chậm hơn lần sau ở Java").

Ngày 765: Runtime khác nhau như thế nào giữa 3 ngôn ngữ

  • Mục tiêu: Có bức tranh so sánh tổng thể trước khi học sâu từng cái.

  • Lý thuyết: JVM (managed runtime, bytecode + JIT), CPython (interpreter thuần, GIL), C++ (không có runtime managed, compile thẳng ra machine code, ABI quyết định mọi thứ).

  • Thực hành: Lập bảng so sánh 3 runtime theo tiêu chí: có JIT không, có GC không, có GIL/tương đương không.

PHẦN A: JVM INTERNALS (Ngày 766–811)

Class Loading (Ngày 766–773)

Ngày 766: ClassLoader Hierarchy

  • Mục tiêu: Hiểu cấu trúc phân cấp load class trong JVM.

  • Lý thuyết: Bootstrap ClassLoader (load core Java class) → Extension/Platform ClassLoader → Application ClassLoader (load class của chính ứng dụng) - mỗi tầng chịu trách nhiệm 1 phạm vi class khác nhau.

  • Thực hành: Viết 1 chương trình Java in ra ClassLoader của String.class và của chính class ứng dụng, quan sát khác biệt.

Ngày 767: Class Loading Process

  • Mục tiêu: Hiểu 3 giai đoạn 1 class trải qua trước khi dùng được.

  • Lý thuyết: Loading (đọc bytecode vào memory) → Linking → Initialization (chạy static initializer).

  • Thực hành: Viết class có static block in log, quan sát thời điểm chính xác nó chạy (khi class được reference lần đầu, không phải lúc JVM khởi động).

Ngày 768: Linking - Verification/Preparation/Resolution

  • Mục tiêu: Hiểu chi tiết giai đoạn Linking.

  • Lý thuyết: Verification (kiểm tra bytecode hợp lệ, không vi phạm an toàn), Preparation (cấp phát memory cho static field, gán giá trị mặc định), Resolution (chuyển symbolic reference thành direct reference).

  • Thực hành: Giải thích tại sao Verification là bước bảo mật quan trọng (ngăn bytecode độc hại/lỗi thời được tạo thủ công).

Ngày 769: Parent Delegation Model

  • Mục tiêu: Hiểu cơ chế đảm bảo tính nhất quán của core class.

  • Lý thuyết: Khi 1 ClassLoader cần load class, nó luôn hỏi ClassLoader cha trước - đảm bảo class core (VD: java.lang.String) luôn được load bởi Bootstrap ClassLoader, không thể bị ghi đè bởi code ứng dụng.

  • Thực hành: Thử tạo 1 class tên java.lang.String tuỳ chỉnh trong code ứng dụng, giải thích vì sao JVM không cho phép (hoặc bỏ qua nó) nhờ Parent Delegation.

Ngày 770: Custom ClassLoader

  • Mục tiêu: Liên hệ lại Plugin System (Giai đoạn 1/10).

  • Lý thuyết: Custom ClassLoader cho phép load class từ nguồn không chuẩn (file ngoài classpath, network, mã hoá) - chính là cơ chế nhiều framework Plugin System Java thật dùng để load plugin động mà không cần restart JVM.

  • Thực hành: So sánh Custom ClassLoader với cách Plugin System đã xây (Ngày 90, dùng reflection) - Custom ClassLoader mạnh hơn vì cho phép cô lập version khác nhau của cùng 1 class.

Ngày 771: Lazy Class Loading

  • Mục tiêu: Hiểu thời điểm chính xác 1 class được load.

  • Lý thuyết: JVM chỉ load class khi thực sự cần (lần đầu reference active use - tạo instance, gọi static method, truy cập static field) - không load toàn bộ classpath lúc khởi động.

  • Thực hành: Viết 2 class A và B (A tham chiếu B nhưng không dùng), verify qua -verbose:class rằng B chỉ được load khi A thực sự dùng tới nó.

Ngày 772: Lab - Viết Custom ClassLoader

  • Mục tiêu: Thực hành trực tiếp.

  • Lý thuyết: Ôn lại defineClass() - method chuyển byte array thành Class object.

  • Thực hành: Viết CustomClassLoader load class từ 1 file .class nằm ngoài classpath chuẩn, tạo instance qua reflection và gọi method.

Ngày 773: Review Class Loading

  • Mục tiêu: Củng cố nhóm.

  • Lý thuyết: Tổng hợp: ClassLoader Hierarchy → Loading/Linking/Initialization → Parent Delegation → Custom ClassLoader.

  • Thực hành: Vẽ sơ đồ tổng thể vòng đời 1 class từ lúc file .class tồn tại trên disk tới lúc sẵn sàng dùng.

Bytecode (Ngày 774–781)

Ngày 774: JVM Bytecode - Giới thiệu

  • Mục tiêu: Hiểu JVM là 1 stack-based virtual machine.

  • Lý thuyết: Không giống CPU thật (register-based), JVM bytecode thao tác trên 1 operand stack - mỗi instruction push/pop giá trị từ stack.

  • Thực hành: Đọc 1 đoạn bytecode đơn giản (a + b), giải thích từng bước push/pop trên operand stack.

Ngày 775: Bytecode Instruction - Ôn sâu invokevirtual/invokeinterface

  • Mục tiêu: Đào sâu hơn kiến thức đã chạm ở Giai đoạn 1 (Ngày 81-82).

  • Lý thuyết: Ôn lại invokevirtual/invokeinterface, giờ thêm invokestatic (gọi static method, không cần dispatch), invokespecial (gọi constructor/private method, biết chính xác method nào).

  • Thực hành: Viết 4 loại method call khác nhau, dùng javap -c xác định đúng instruction nào được dùng cho mỗi loại.

Ngày 776: Constant Pool

  • Mục tiêu: Hiểu nơi lưu trữ literal và symbolic reference.

  • Lý thuyết: Mỗi class file có 1 Constant Pool chứa string literal, tên class/method/field - bytecode reference vào Constant Pool bằng index thay vì lưu trực tiếp giá trị.

  • Thực hành: Dùng javap -v xem Constant Pool của 1 class đơn giản, tìm entry tương ứng với 1 string literal trong code.

Ngày 777: Bytecode cho Control Flow

  • Mục tiêu: Hiểu if/loop compile ra gì.

  • Lý thuyết: if/while compile thành conditional jump instruction (ifeq, ifne, goto) - không có khái niệm block như source code, chỉ có label và jump.

  • Thực hành: Viết 1 vòng for loop đơn giản, dùng javap -c đọc bytecode, vẽ lại control flow graph từ các jump instruction.

Ngày 778: javap - Công cụ đọc Bytecode

  • Mục tiêu: Thành thạo công cụ đã dùng nhiều lần trong giai đoạn này.

  • Lý thuyết: Các flag hữu ích: -c (disassemble), -v (verbose, gồm Constant Pool), -p (hiện cả private member).

  • Thực hành: Dùng javap -v phân tích đầy đủ 1 class có field, method, constructor, ghi chú lại ý nghĩa các phần trong output.

Ngày 779: Bytecode Manipulation - ASM

  • Mục tiêu: Liên hệ lại CGLIB đã gặp ở Spring AOP (Giai đoạn 10).

  • Lý thuyết: Thư viện như ASM cho phép sinh/sửa bytecode lúc runtime - chính là cách CGLIB tạo subclass proxy động, cách nhiều mocking framework (Mockito) tạo mock object.

  • Thực hành: Đọc ví dụ code ASM đơn giản sinh ra 1 class "Hello World" hoàn toàn bằng bytecode (không qua compiler .java).

Ngày 780: Lab - Sinh Class Runtime bằng ASM

  • Mục tiêu: Thực hành trực tiếp kỹ thuật đứng sau nhiều framework.

  • Lý thuyết: Ôn lại API cơ bản của ASM (ClassWriter, MethodVisitor).

  • Thực hành: Dùng ASM sinh 1 class đơn giản có 1 method trả về hằng số, load bằng Custom ClassLoader (Ngày 772) và gọi thử.

Ngày 781: Review Bytecode

  • Mục tiêu: Củng cố nhóm Bytecode.

  • Lý thuyết: Tổng hợp: Stack-based machine, instruction chính, Constant Pool, control flow, ASM.

  • Thực hành: Viết tổng kết liên hệ lại: giờ đã hiểu rõ hơn Spring AOP (Giai đoạn 10) thực sự "tạo proxy" nghĩa là gì ở tầng bytecode.

JIT Compiler (Ngày 782–791)

Ngày 782: Interpreter vs JIT

  • Mục tiêu: Hiểu vì sao JVM cần cả 2 cách thực thi.

  • Lý thuyết: Interpreter (thực thi bytecode trực tiếp, khởi động nhanh nhưng chạy chậm) vs JIT (Just-In-Time compile bytecode thành machine code native, khởi động chậm hơn nhưng chạy nhanh hơn nhiều sau đó) - JVM dùng cả 2, bắt đầu bằng interpreter rồi JIT compile method "nóng".

  • Thực hành: Vẽ sơ đồ minh hoạ trade-off: chỉ dùng Interpreter (chậm đều) vs chỉ dùng JIT (chậm lúc khởi động) vs kết hợp cả 2 (JVM thực tế).

Ngày 783: Tiered Compilation

  • Mục tiêu: Hiểu JVM hiện đại không chỉ có 1 mức JIT.

  • Lý thuyết: C1 (client compiler, compile nhanh, tối ưu ít) cho method mới bắt đầu nóng, C2 (server compiler, compile chậm hơn nhưng tối ưu mạnh) cho method thực sự rất nóng - tiered compilation chuyển dần qua các mức.

  • Thực hành: Đọc tài liệu Tiered Compilation, vẽ sơ đồ các level (0-4) và điều kiện chuyển giữa chúng.

Ngày 784: Hot Method Detection

  • Mục tiêu: Hiểu JVM quyết định "method nào đáng compile" ra sao.

  • Lý thuyết: JVM đếm số lần method được gọi (invocation counter) và số lần backward branch trong loop - vượt ngưỡng thì method được đưa vào hàng đợi compile.

  • Thực hành: Viết 1 method chạy trong loop rất nhiều lần, dùng flag -XX:+PrintCompilation quan sát thời điểm nó được JIT compile.

Ngày 785: Inlining

  • Mục tiêu: Học tối ưu quan trọng nhất của JIT.

  • Lý thuyết: Inlining thay lời gọi method bằng chính thân method đó ngay tại chỗ gọi - loại bỏ overhead gọi hàm và mở ra cơ hội tối ưu tiếp theo (JIT có thể tối ưu toàn bộ đoạn code đã inline như 1 khối).

  • Thực hành: Viết 1 method nhỏ được gọi trong loop nóng, giải thích tại sao JIT có xu hướng inline nó (method nhỏ, gọi thường xuyên).

Ngày 786: Escape Analysis

  • Mục tiêu: Học tối ưu bất ngờ nhất - object có thể không cần cấp phát trên heap.

  • Lý thuyết: Nếu JIT chứng minh được 1 object không bao giờ "thoát" khỏi method (không được return, không lưu vào field ngoài) - nó có thể cấp phát object đó trên stack thay vì heap, giảm tải cho GC.

  • Thực hành: Viết 1 method tạo object tạm chỉ dùng nội bộ, giải thích lý thuyết vì sao JIT có thể áp dụng Escape Analysis cho trường hợp này (liên hệ lại Stack vs Heap, Giai đoạn 0).

Ngày 787: JIT Deoptimization

  • Mục tiêu: Hiểu JIT không phải lúc nào cũng đúng, và cách nó tự sửa sai.

  • Lý thuyết: JIT tối ưu dựa trên giả định (VD: 1 method luôn nhận cùng 1 type cụ thể) - nếu giả định sai (VD: type khác xuất hiện), JVM deoptimize (quay lại chạy bằng interpreter) rồi compile lại với giả định mới.

  • Thực hành: Vẽ sơ đồ minh hoạ: JIT compile method giả định input luôn là Integer, sau đó nhận Double → deoptimize → compile lại tổng quát hơn.

Ngày 788: JIT Warmup

  • Mục tiêu: Hiểu ảnh hưởng thực tế lên benchmark và production.

  • Lý thuyết: Ứng dụng Java cần "warm up" (chạy đủ nhiều để JIT compile các method nóng) trước khi đạt hiệu năng tối đa - ảnh hưởng trực tiếp tới benchmark sai (đo ngay từ đầu) và tới thời gian khởi động service thật.

  • Thực hành: Viết benchmark thô đo thời gian chạy 1 method qua nhiều vòng lặp liên tiếp, quan sát các vòng đầu chậm hơn đáng kể so với các vòng sau.

Ngày 789: Lab - Viết Benchmark bằng JMH

  • Mục tiêu: Học công cụ benchmark chuẩn cho Java, tránh sai lầm đo lường ở Ngày 788.

  • Lý thuyết: JMH (Java Microbenchmark Harness) tự động xử lý warmup, tránh các pitfall benchmark phổ biến (dead code elimination, constant folding).

  • Thực hành: Viết 1 benchmark JMH đơn giản, so sánh với cách đo thô ở Ngày 788, giải thích sự khác biệt về độ tin cậy.

Ngày 790: Lab - Quan sát JIT Compilation Log

  • Mục tiêu: Thấy JIT hoạt động thật, không chỉ qua lý thuyết.

  • Lý thuyết: Ôn lại flag -XX:+PrintCompilation.

  • Thực hành: Chạy 1 ứng dụng thật (VD: 3-service project) với flag JIT logging, quan sát method nào được compile ở level nào theo thời gian.

Ngày 791: Review JIT

  • Mục tiêu: Củng cố nhóm JIT - nhóm phức tạp nhất của JVM Track.

  • Lý thuyết: Tổng hợp: Interpreter → Tiered Compilation → Hot Method Detection → Inlining → Escape Analysis → Deoptimization → Warmup.

  • Thực hành: Viết tổng kết: giải thích cho đồng nghiệp vì sao "benchmark Java cần warmup" bằng chính những gì vừa học, không chỉ nói suông "JIT cần thời gian".

Garbage Collector (Ngày 792–803)

Ngày 792: GC - Giới thiệu Mark and Sweep

  • Mục tiêu: Ôn lại và đào sâu GC đã chạm ở Giai đoạn 0 (Ngày 21).

  • Lý thuyết: Mark (đánh dấu mọi object còn reachable từ GC Root) → Sweep (giải phóng mọi object không được đánh dấu).

  • Thực hành: Vẽ sơ đồ object graph đơn giản với vài object không còn reachable, mô phỏng thủ công quá trình Mark and Sweep.

Ngày 793: Generational Hypothesis

  • Mục tiêu: Hiểu giả thuyết nền tảng của GC hiện đại.

  • Lý thuyết: Hầu hết object "chết trẻ" (weak generational hypothesis) - object mới tạo có xu hướng bị thu hồi rất nhanh, object sống lâu thì có xu hướng sống rất lâu - chia heap thành generation để tối ưu dựa trên quan sát này.

  • Thực hành: Giải thích tại sao giả thuyết này đúng với hầu hết ứng dụng thực tế (VD: object tạm trong 1 request HTTP thường chết ngay sau khi request xử lý xong).

Ngày 794: Young Generation - Eden & Survivor Space

  • Mục tiêu: Hiểu cấu trúc chi tiết vùng chứa object mới.

  • Lý thuyết: Eden (nơi object mới được cấp phát), Survivor Space (2 vùng S0/S1, object sống sót qua Minor GC được chuyển qua lại giữa 2 vùng này).

  • Thực hành: Vẽ sơ đồ Young Generation với Eden + 2 Survivor space, mô tả luồng object di chuyển qua các lần Minor GC.

Ngày 795: Minor GC - Copying Collection

  • Mục tiêu: Hiểu thuật toán GC dùng cho Young Generation.

  • Lý thuyết: Copying Collection - copy object còn sống sang Survivor space trống, toàn bộ vùng cũ coi như trống ngay lập tức (không cần quét tìm chỗ trống như Sweep) - rất nhanh vì Young Generation thường ít object sống sót.

  • Thực hành: So sánh chi phí Copying Collection (chỉ tốn chi phí theo số object SỐNG) với Mark-Sweep (tốn chi phí theo TOÀN BỘ heap), giải thích vì sao Copying phù hợp Young Generation.

Ngày 796: Old Generation - Major/Full GC

  • Mục tiêu: Hiểu GC ở vùng chứa object sống lâu.

  • Lý thuyết: Object sống sót qua đủ số lần Minor GC được "promote" lên Old Generation - GC ở đây (Major GC) tốn kém hơn nhiều vì Old Generation thường lớn và có nhiều object sống.

  • Thực hành: Giải thích vì sao "Full GC" (GC cả Young + Old) thường là nguyên nhân chính gây ra pause time lớn ảnh hưởng tới latency ứng dụng.

Ngày 797: Serial GC & Parallel GC

  • Mục tiêu: Học 2 GC đơn giản nhất.

  • Lý thuyết: Serial GC (1 thread duy nhất làm GC, dừng toàn bộ ứng dụng - phù hợp ứng dụng nhỏ), Parallel GC (nhiều thread cùng làm GC song song, vẫn dừng ứng dụng nhưng nhanh hơn Serial).

  • Thực hành: Lập bảng so sánh Serial vs Parallel GC theo tiêu chí: số thread GC, mức độ dừng ứng dụng (stop-the-world), use case phù hợp.

Ngày 798: CMS GC (Legacy)

  • Mục tiêu: Hiểu GC concurrent đầu tiên phổ biến (dù đã deprecated), để hiểu lịch sử phát triển GC.

  • Lý thuyết: Concurrent Mark Sweep - cố gắng làm phần Mark chạy song song với ứng dụng (concurrent) thay vì dừng hoàn toàn, giảm pause time nhưng có vấn đề fragmentation (không compact).

  • Thực hành: Giải thích vấn đề fragmentation của CMS dẫn tới việc nó bị thay thế bởi G1 trong các JDK hiện đại.

Ngày 799: G1 GC

  • Mục tiêu: Học GC mặc định hiện đại nhất được dùng phổ biến.

  • Lý thuyết: G1 (Garbage First) chia heap thành nhiều region nhỏ thay vì 2 vùng lớn cố định (Young/Old truyền thống), ưu tiên thu gom region có nhiều rác nhất trước ("Garbage First") - cân bằng được throughput và pause time.

  • Thực hành: Vẽ sơ đồ heap G1 với nhiều region nhỏ (mỗi region có thể là Eden/Survivor/Old tuỳ thời điểm), giải thích tính linh hoạt so với Young/Old cố định.

Ngày 800: ZGC & Shenandoah

  • Mục tiêu: Học GC low-latency thế hệ mới nhất.

  • Lý thuyết: ZGC/Shenandoah đạt pause time gần như hằng số (dưới 10ms) dù heap lớn cỡ nào, bằng cách làm hầu hết công việc (bao gồm cả compact) concurrent với ứng dụng - đánh đổi bằng throughput tổng thể thấp hơn 1 chút.

  • Thực hành: Lập bảng so sánh G1 vs ZGC theo tiêu chí: pause time, throughput, độ trưởng thành/phổ biến.

Ngày 801: GC Tuning

  • Mục tiêu: Học cách đọc GC log và chọn GC phù hợp cho use case thực tế.

  • Lý thuyết: -Xlog:gc (hoặc -XX:+PrintGCDetails ở JDK cũ hơn) cho thấy tần suất, thời gian, kích thước heap trước/sau mỗi lần GC - dựa vào đó quyết định tăng heap size, đổi GC algorithm, hay tối ưu code giảm rác.

  • Thực hành: Chạy 3-service project với GC logging bật, đọc log, xác định GC nào đang chạy và pause time trung bình.

Ngày 802: Lab - Benchmark các loại GC

  • Mục tiêu: So sánh thực tế thay vì chỉ dựa lý thuyết.

  • Lý thuyết: Ôn lại các flag chọn GC (-XX:+UseG1GC, -XX:+UseZGC...).

  • Thực hành: Chạy cùng 1 workload (VD: benchmark tạo nhiều object tạm) với Parallel GC, G1 GC, ZGC, so sánh throughput và pause time.

Ngày 803: Review GC

  • Mục tiêu: Củng cố nhóm GC - nhóm dài nhất trong JVM Track.

  • Lý thuyết: Tổng hợp: Mark-Sweep cơ bản → Generational Hypothesis → Young/Old Generation → các thuật toán GC (Serial/Parallel/CMS/G1/ZGC) → GC Tuning.

  • Thực hành: Viết bảng quyết định: với 1 ứng dụng có yêu cầu latency thấp (VD: trading system) vs 1 ứng dụng batch processing (throughput quan trọng hơn), chọn GC nào.

JVM Memory Model (Ngày 804–807)

Ngày 804: JMM - Ôn sâu hơn

  • Mục tiêu: Quay lại Happens-Before (Giai đoạn 5, Ngày 255) với hiểu biết mới về JIT.

  • Lý thuyết: JIT có thể reorder instruction để tối ưu - JMM (Java Memory Model) định nghĩa chính xác những reordering nào được phép, những happens-before relationship nào JIT phải tôn trọng.

  • Thực hành: Giải thích tại sao thiếu volatile/synchronized không chỉ là vấn đề "cache CPU" (Giai đoạn 0/5) mà còn là vấn đề JIT có thể tối ưu sai nếu không có rào cản rõ ràng.

Ngày 805: volatile - Implementation thật

  • Mục tiêu: Hiểu volatile làm gì ở tầng dưới, không chỉ "đảm bảo visibility".

  • Lý thuyết: volatile chèn memory barrier (rào chắn ngăn compiler/CPU reorder qua nó) - cả ở tầng JIT compilation và tầng CPU instruction thật.

  • Thực hành: Đọc bytecode/assembly (nếu có công cụ) của 1 field volatile so với field thường, tìm memory barrier instruction được chèn thêm.

Ngày 806: Final Field Semantics

  • Mục tiêu: Hiểu đảm bảo đặc biệt của final field trong JMM.

  • Lý thuyết: JMM đảm bảo nếu constructor không "leak" this ra ngoài (object chưa hoàn thành khởi tạo), thì mọi thread khác thấy final field đã được khởi tạo đầy đủ ngay khi thấy reference tới object đó - không cần thêm đồng bộ hoá.

  • Thực hành: Viết ví dụ minh hoạ đảm bảo này: 1 object có final field publish qua biến chia sẻ, thread khác đọc luôn thấy giá trị đã khởi tạo đúng.

Ngày 807: Review Memory Model

  • Mục tiêu: Chốt lại nhóm.

  • Lý thuyết: Tổng hợp: JMM + JIT reordering, volatile là memory barrier, final field guarantee.

  • Thực hành: Viết tổng kết liên hệ ngược lại toàn bộ Concurrency (Giai đoạn 5) dưới góc nhìn JIT vừa học thêm.

Java Agent (Ngày 808–811)

Ngày 808: Java Agent - Giới thiệu

  • Mục tiêu: Học cơ chế instrumentation mạnh nhất của JVM.

  • Lý thuyết: Java Agent (Instrumentation API) cho phép can thiệp vào bytecode của MỌI class được load, kể cả class trong thư viện bên thứ 3 - nền tảng của APM tool (New Relic, Datadog agent), profiler.

  • Thực hành: Đọc tài liệu java.lang.instrument, tóm tắt khả năng chính (transform bytecode trước khi class được định nghĩa).

Ngày 809: premain & agentmain

  • Mục tiêu: Hiểu 2 cách agent được nạp vào JVM.

  • Lý thuyết: premain (agent nạp lúc JVM khởi động, qua flag -javaagent), agentmain (agent nạp vào JVM đang chạy - attach API).

  • Thực hành: Viết agent tối giản với premain, in log khi bất kỳ class nào được load.

Ngày 810: Lab - Viết Java Agent đo thời gian Method

  • Mục tiêu: Thực hành 1 use case thực tế, liên hệ lại AOP (Giai đoạn 10) nhưng ở tầng bytecode thay vì proxy.

  • Lý thuyết: Ôn lại ASM (Ngày 779-780) - agent thường dùng ASM để transform bytecode, chèn code đo thời gian vào đầu/cuối method.

  • Thực hành: Viết Java Agent dùng ASM chèn code log thời gian thực thi vào mọi method của 1 class chỉ định, chạy thử với Order Service.

Ngày 811: Review Java Agent - Chốt JVM Track

  • Mục tiêu: Tổng kết toàn bộ JVM Track (46 ngày).

  • Lý thuyết: Nhìn lại: Class Loading → Bytecode → JIT → GC → Memory Model → Java Agent - đây chính là toàn bộ "hộp đen" mà mọi code Java/Spring đã viết từ đầu roadmap chạy bên trong.

  • Thực hành: Viết báo cáo retrospective JVM Track: liên hệ Spring AOP (CGLIB dùng ASM-like technique), Spring Bean lifecycle (liên hệ Class Loading/GC roots).

PHẦN B: CPYTHON INTERNALS (Ngày 812–851)

PyObject (Ngày 812–819)

Ngày 812: PyObject - Cấu trúc cơ bản

  • Mục tiêu: Hiểu "nguyên tử" của mọi thứ trong CPython.

  • Lý thuyết: PyObject là struct C gốc mọi object Python kế thừa - chứa ob_refcnt (reference count) và ob_type (con trỏ tới type object).

  • Thực hành: Vẽ sơ đồ struct PyObject cơ bản, giải thích 2 field chính.

Ngày 813: Mọi thứ là PyObject

  • Mục tiêu: Hiểu tính đồng nhất đặc trưng của Python.

  • Lý thuyết: Integer, string, function, class, module - tất cả đều là PyObject, khác Java (có primitive type riêng) hay C++ (có kiểu giá trị riêng biệt khỏi object).

  • Thực hành: Dùng id()/type() trong Python REPL kiểm tra: integer, function, class đều có địa chỉ và type object riêng, xác nhận chúng đều "là object".

Ngày 814: Reference Counting - Đào sâu

  • Mục tiêu: Ôn và đào sâu Giai đoạn 0 (Ngày 22).

  • Lý thuyết: Py_INCREF/Py_DECREF - mọi thao tác gán/truyền tham số/lưu vào container đều tăng/giảm refcount, refcount về 0 thì object bị giải phóng ngay lập tức (khác GC generational của Java, refcounting giải phóng tức thời, không cần đợi chu kỳ GC).

  • Thực hành: Viết ví dụ đơn giản với sys.getrefcount(), giải thích chính xác từng thao tác nào làm refcount tăng/giảm.

Ngày 815: PyObject cho Container

  • Mục tiêu: Hiểu container Python thực chất chứa gì.

  • Lý thuyết: PyListObject chứa mảng con trỏ PyObject* (không chứa giá trị trực tiếp) - giải thích vì sao Python list có thể chứa phần tử với type khác nhau (mỗi phần tử chỉ là 1 con trỏ).

  • Thực hành: Vẽ sơ đồ minh hoạ 1 list Python [1, "hello", 3.14] - mỗi phần tử là con trỏ tới 1 PyObject riêng biệt ở vị trí bộ nhớ khác nhau.

Ngày 816: Small Integer Caching

  • Mục tiêu: Học 1 tối ưu thú vị của CPython.

  • Lý thuyết: CPython cache sẵn các integer nhỏ (-5 đến 256) - mọi biến gán giá trị trong khoảng này đều trỏ tới cùng 1 object, tránh cấp phát lặp lại.

  • Thực hành: Dùng is operator kiểm tra a = 100; b = 100; a is b (True, do cache) vs a = 1000; b = 1000; a is b (thường False, ngoài khoảng cache), giải thích kết quả.

Ngày 817: String Interning

  • Mục tiêu: Học tối ưu tương tự cho string.

  • Lý thuyết: CPython intern (cache và tái sử dụng) 1 số string, đặc biệt identifier-like string (tên biến, string literal ngắn trong code) - giúp so sánh string nhanh hơn bằng cách so sánh con trỏ trước khi so sánh nội dung.

  • Thực hành: Kiểm tra is operator cho vài string khác nhau (string literal ngắn vs string được tạo động qua +/format), giải thích khi nào interning xảy ra.

Ngày 818: Lab - Xem Raw PyObject Structure

  • Mục tiêu: Thấy tận mắt cấu trúc đã học bằng công cụ thật.

  • Lý thuyết: Ôn lại ctypes cho phép truy cập raw memory từ Python.

  • Thực hành: Dùng ctypes đọc ob_refcnt trực tiếp từ 1 object Python, verify khớp với sys.getrefcount().

Ngày 819: Review PyObject

  • Mục tiêu: Củng cố nhóm.

  • Lý thuyết: Tổng hợp: PyObject struct → Reference Counting → Container → tối ưu (integer caching, string interning).

  • Thực hành: Viết tổng kết so sánh PyObject model với JVM object model (Ngày 812-819 vs Java object đã học Giai đoạn 1/JVM Track).

TypeObject (Ngày 820–827)

Ngày 820: PyTypeObject - "Class của Class"

  • Mục tiêu: Hiểu cách Python biểu diễn chính khái niệm "type".

  • Lý thuyết: PyTypeObject mô tả 1 type (VD: int, list, hay class tự định nghĩa) - bản thân nó cũng là 1 PyObject (type của inttype).

  • Thực hành: Trong Python REPL, kiểm tra type(int) (trả về type) và type(type) (trả về chính nó), giải thích vòng lặp tự tham chiếu này.

Ngày 821: Type Slot

  • Mục tiêu: Hiểu cách type object định nghĩa hành vi.

  • Lý thuyết: tp_new (tạo instance), tp_init (khởi tạo, tương ứng __init__), tp_dealloc (giải phóng) - mỗi slot là 1 con trỏ hàm C, chính là cơ chế bên dưới __new__/__init__/__del__ đã học Giai đoạn 1.

  • Thực hành: Vẽ sơ đồ ánh xạ: method Python (__init__) → type slot tương ứng (tp_init) → hàm C thực thi.

Ngày 822: MRO - Implementation thật

  • Mục tiêu: Đào sâu Method Resolution Order đã học Giai đoạn 1 (Ngày 84).

  • Lý thuyết: PyTypeObject lưu sẵn tp_mro (tuple chứa MRO đã tính bằng thuật toán C3 linearization) - không tính lại mỗi lần lookup attribute, tối ưu hiệu năng.

  • Thực hành: Dùng ClassName.__mro__ kiểm tra lại ví dụ đa kế thừa từ Giai đoạn 1, giải thích vì sao MRO được cache thay vì tính lại mỗi lần.

Ngày 823: slots

  • Mục tiêu: Học tối ưu memory quan trọng cho object có nhiều instance.

  • Lý thuyết: Class thông thường lưu attribute trong __dict__ (linh hoạt nhưng tốn memory) - __slots__ khai báo trước danh sách attribute cố định, bỏ __dict__, tiết kiệm đáng kể memory khi có hàng triệu instance.

  • Thực hành: So sánh kích thước memory của 1 class thường vs class dùng __slots__ (dùng sys.getsizeof() hoặc thư viện đo memory) cho cùng số lượng instance.

Ngày 824: Metaclass

  • Mục tiêu: Hiểu "type tạo ra type" - khái niệm nâng cao nhất của OOP Python.

  • Lý thuyết: Metaclass là class của class (mặc định là type) - override metaclass cho phép can thiệp vào chính quá trình tạo ra class (không phải tạo instance của class).

  • Thực hành: Đọc ví dụ 1 metaclass đơn giản override __new__ để tự động thêm 1 method vào mọi class dùng metaclass đó.

Ngày 825: Lab - Viết Metaclass Tuỳ chỉnh

  • Mục tiêu: Thực hành trực tiếp, liên hệ ORM (SQLAlchemy declarative, Giai đoạn 10).

  • Lý thuyết: Ôn lại: nhiều ORM dùng metaclass để tự động convert class attribute thành column mapping lúc class được định nghĩa.

  • Thực hành: Viết 1 metaclass đơn giản tự động đăng ký mọi class dùng nó vào 1 registry toàn cục (tương tự cách ORM đăng ký model).

Ngày 826: Duck Typing - Implementation

  • Mục tiêu: Hiểu cơ chế thực sự đứng sau duck typing đã học Giai đoạn 1 (Ngày 65).

  • Lý thuyết: Attribute lookup của Python tìm kiếm động qua __dict__/__slots__/type slot lúc runtime - không có type checking tĩnh, nên bất kỳ object nào có đúng attribute/method cần thiết đều "hoạt động được", không cần khai báo interface.

  • Thực hành: Giải thích lại ví dụ quack() đã làm ở Giai đoạn 1 dưới góc độ: Python chỉ cần tìm thấy attribute quack trong __dict__ của object lúc gọi, không quan tâm object đó "là gì".

Ngày 827: Review TypeObject

  • Mục tiêu: Củng cố nhóm TypeObject.

  • Lý thuyết: Tổng hợp: PyTypeObject → Type Slot → MRO → __slots__ → Metaclass → Duck typing.

  • Thực hành: Viết tổng kết liên hệ ngược Descriptor Protocol (sẽ học Ngày 836-839) - Type Slot và Descriptor cùng là cơ chế CPython dùng để customize attribute access.

CPython Bytecode (Ngày 828–835)

Ngày 828: CPython Bytecode - Giới thiệu

  • Mục tiêu: So sánh với JVM Bytecode đã học ở Phần A.

  • Lý thuyết: CPython cũng compile source thành bytecode trước khi thực thi (không phải interpret trực tiếp source) - nhưng khác JVM, CPython bytecode không được JIT compile mặc định (thuần interpreter).

  • Thực hành: Dùng module dis, disassemble 1 hàm Python đơn giản, so sánh cấu trúc output với javap -c đã dùng ở JVM Track.

Ngày 829: Bytecode Instruction cơ bản

  • Mục tiêu: Đọc hiểu các instruction phổ biến nhất.

  • Lý thuyết: LOAD_FAST/STORE_FAST (biến local), LOAD_GLOBAL (biến global), LOAD_CONST (hằng số) - CPython cũng là stack-based như JVM.

  • Thực hành: Viết 1 hàm với vài biến local, dùng dis.dis() xác định đúng instruction cho từng thao tác đọc/ghi biến.

Ngày 830: Bytecode cho Function Call

  • Mục tiêu: Hiểu lời gọi hàm biên dịch ra gì.

  • Lý thuyết: CALL_FUNCTION (hoặc CALL ở Python mới hơn) - chuẩn bị argument trên stack rồi gọi.

  • Thực hành: Dùng dis.dis() cho 1 lời gọi hàm có nhiều tham số, giải thích thứ tự các instruction chuẩn bị argument.

Ngày 831: Frame Object

  • Mục tiêu: Hiểu ngữ cảnh thực thi của mỗi lời gọi hàm.

  • Lý thuyết: Mỗi lần gọi hàm tạo 1 Frame object chứa: local variable, bytecode đang chạy, instruction pointer - liên hệ lại Stack Frame đã học ở Giai đoạn 0 nhưng ở tầng CPython thay vì tầng CPU.

  • Thực hành: Dùng module inspect/sys._getframe() để lấy Frame object hiện tại, in ra tên biến local trong đó.

Ngày 832: Interpreter Loop

  • Mục tiêu: Hiểu vòng lặp trung tâm thực thi mọi bytecode Python.

  • Lý thuyết: ceval.c chứa vòng lặp lớn (giant switch statement) xử lý từng loại bytecode instruction - đây chính là "trái tim" của CPython interpreter.

  • Thực hành: Đọc mô tả tổng quan (không cần đọc toàn bộ source) về cấu trúc ceval.c, giải thích vì sao nó được coi là phần code performance-critical nhất của CPython.

Ngày 833: Lab - Đọc dis Output

  • Mục tiêu: Luyện kỹ năng đọc bytecode thực tế.

  • Lý thuyết: Ôn lại toàn bộ instruction đã học tuần này.

  • Thực hành: Disassemble 3 hàm Python có độ phức tạp tăng dần (biến đơn giản → loop → function call lồng nhau), giải thích đầy đủ luồng thực thi qua bytecode.

Ngày 834: So sánh CPython Bytecode vs JVM Bytecode

  • Mục tiêu: Tổng hợp so sánh giữa 2 runtime.

  • Lý thuyết: Cả 2 đều stack-based, nhưng JVM bytecode được JIT compile thành machine code (nhanh dần theo thời gian), CPython bytecode luôn được interpret (tốc độ ổn định nhưng chậm hơn).

  • Thực hành: Lập bảng so sánh đầy đủ: stack-based (cả 2), có JIT không, tốc độ tương đối, công cụ đọc bytecode (javap vs dis).

Ngày 835: Review Bytecode

  • Mục tiêu: Củng cố nhóm.

  • Lý thuyết: Tổng hợp: Bytecode instruction → Function call → Frame → Interpreter loop.

  • Thực hành: Viết tổng kết giải thích vì sao hiểu bytecode giúp optimize code Python thực tế (VD: biết LOAD_FAST nhanh hơn LOAD_GLOBAL → ưu tiên dùng local variable trong hot loop).

Descriptor Protocol (Ngày 836–839)

Ngày 836: Descriptor Protocol - Ôn sâu

  • Mục tiêu: Đào sâu kiến thức đã chạm Giai đoạn 1 (Ngày 85).

  • Lý thuyết: __get__/__set__/__delete__ - khi 1 attribute lookup xảy ra, Python kiểm tra xem attribute đó có phải descriptor không (có định nghĩa các method này), nếu có thì gọi qua descriptor thay vì trả trực tiếp từ __dict__.

  • Thực hành: Vẽ sơ đồ luồng: obj.attr → Python kiểm tra type có descriptor cho attr không → nếu có, gọi __get__ thay vì đọc __dict__ trực tiếp.

Ngày 837: property - Implementation qua Descriptor

  • Mục tiêu: Hiểu property (builtin quen thuộc) thực chất là 1 descriptor.

  • Lý thuyết: property là 1 non-data/data descriptor implement sẵn __get__/__set__ gọi tới getter/setter function do người dùng định nghĩa - mọi thứ @property làm được, tự viết descriptor cũng làm được.

  • Thực hành: Viết lại 1 ví dụ dùng @property bằng cách tự implement descriptor class thủ công, verify hành vi giống hệt nhau.

Ngày 838: Lab - Viết ORM-like Descriptor

  • Mục tiêu: Liên hệ trực tiếp SQLAlchemy (Giai đoạn 10) - hiểu cách nó implement Column mapping.

  • Lý thuyết: Ôn lại: ORM dùng descriptor để mỗi khi bạn gán order.status = "PAID", descriptor tự động ghi nhận thay đổi đó vào "dirty" tracking của Session (Unit of Work, Giai đoạn 10).

  • Thực hành: Viết 1 descriptor TrackedField tự động ghi log mỗi khi giá trị field bị thay đổi (mô phỏng đơn giản cơ chế dirty tracking của SQLAlchemy Session).

Ngày 839: Review Descriptor

  • Mục tiêu: Chốt lại nhóm Descriptor.

  • Lý thuyết: Tổng hợp: Descriptor Protocol → property → ứng dụng thực tế trong ORM.

  • Thực hành: Viết tổng kết: giờ đã hiểu rõ "phép màu" của @property và ORM Column mapping không còn là hộp đen.

GIL (Ngày 840–845)

Ngày 840: GIL - Lịch sử

  • Mục tiêu: Ôn và đào sâu GIL đã học Giai đoạn 5 (Ngày 231).

  • Lý thuyết: GIL tồn tại vì CPython dùng reference counting (Ngày 814) - nếu nhiều thread cùng tăng/giảm refcount đồng thời mà không có lock, sẽ gây race condition; GIL là giải pháp đơn giản (dù hạn chế) để tránh việc phải lock từng object riêng lẻ.

  • Thực hành: Giải thích tại sao "chỉ cần 1 lock global" lại đơn giản hơn nhiều so với việc mỗi PyObject tự có lock riêng (fine-grained locking) - và tại sao đơn giản hơn không có nghĩa là tốt hơn cho performance đa lõi.

Ngày 841: GIL Implementation

  • Mục tiêu: Hiểu GIL hoạt động thế nào ở tầng kỹ thuật.

  • Lý thuyết: GIL không phải giữ mãi mãi bởi 1 thread - CPython dùng switch interval (mặc định theo số instruction hoặc theo thời gian) để định kỳ nhả GIL cho thread khác có cơ hội chạy.

  • Thực hành: Đọc tài liệu sys.setswitchinterval(), giải thích ảnh hưởng của việc tăng/giảm switch interval lên độ công bằng giữa các thread.

Ngày 842: GIL và C Extension

  • Mục tiêu: Hiểu cách C extension "thoát" khỏi giới hạn GIL.

  • Lý thuyết: Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS - C extension có thể chủ động nhả GIL khi làm việc nặng không cần đụng tới Python object (VD: tính toán số học thuần, I/O) - đây là lý do NumPy/nhiều thư viện C extension vẫn tận dụng được đa lõi dù có GIL.

  • Thực hành: Đọc ví dụ code C extension đơn giản nhả GIL trước khi gọi 1 hàm C tính toán nặng, giải thích tại sao an toàn khi làm vậy.

Ngày 843: Free-threaded Python (PEP 703)

  • Mục tiêu: Biết hướng phát triển tương lai của CPython.

  • Lý thuyết: PEP 703 đề xuất loại bỏ GIL hoàn toàn (free-threaded build) bằng cách chuyển sang cơ chế reference counting an toàn hơn (biased reference counting) - đánh đổi bằng việc single-thread code chạy chậm hơn 1 chút.

  • Thực hành: Đọc tóm tắt PEP 703, ghi chú lại trade-off chính và trạng thái hiện tại (thử nghiệm/ổn định) tại thời điểm học.

Ngày 844: Lab - Viết C Extension nhả GIL

  • Mục tiêu: Thực hành trực tiếp kỹ thuật đã học.

  • Lý thuyết: Ôn lại cấu trúc cơ bản 1 C extension Python (dùng CPython C API hoặc pybind11 để đơn giản hoá).

  • Thực hành: Viết 1 C extension đơn giản tính toán nặng (VD: tính số nguyên tố), nhả GIL trong lúc tính, benchmark so sánh với việc chạy nhiều thread Python thuần (Giai đoạn 5, Ngày 231) cho cùng bài toán.

Ngày 845: Review GIL

  • Mục tiêu: Chốt lại nhóm GIL với hiểu biết sâu hơn nhiều so với Giai đoạn 5.

  • Lý thuyết: Tổng hợp: lý do tồn tại (refcounting) → implementation (switch interval) → cách thoát (C extension) → tương lai (PEP 703).

  • Thực hành: Viết tổng kết: giải thích chính xác tại sao NumPy tận dụng được đa lõi dù Python có GIL - dựa trên cơ chế Py_BEGIN_ALLOW_THREADS vừa học.

CPython Garbage Collector (Ngày 846–851)

Ngày 846: CPython GC - Đào sâu

  • Mục tiêu: Ôn và đào sâu Giai đoạn 0 (Ngày 23).

  • Lý thuyết: Reference Counting xử lý phần lớn việc dọn rác (tức thời, đơn giản), nhưng không tự giải quyết được reference cycle - CPython có thêm 1 GC riêng (generational, giống ý tưởng JVM nhưng đơn giản hơn nhiều) chỉ để xử lý cycle.

  • Thực hành: Vẽ sơ đồ minh hoạ: object A và B tham chiếu vòng tròn lẫn nhau, dù không còn ai reference từ bên ngoài, refcount của A và B vẫn > 0 (không về 0) - cần GC riêng để phát hiện.

Ngày 847: Cycle Detection Algorithm

  • Mục tiêu: Hiểu thuật toán CPython dùng để tìm cycle.

  • Lý thuyết: CPython dùng biến thể Mark-Sweep chỉ chạy trên các object "container" (có khả năng tạo cycle - list, dict, object thường; không chạy trên int/string vốn không thể tạo cycle) - giảm phạm vi cần quét so với quét toàn bộ heap.

  • Thực hành: Giải thích tại sao chỉ cần theo dõi container object mà không cần theo dõi mọi PyObject, liên hệ lại Ngày 815.

Ngày 848: GC Threshold & Generation trong CPython

  • Mục tiêu: Hiểu CPython cũng có khái niệm generation, dù đơn giản hơn JVM.

  • Lý thuyết: 3 generation (0, 1, 2), object mới tạo ở generation 0, sống sót qua GC thì promote lên generation cao hơn - mỗi generation có ngưỡng số object khác nhau để trigger GC (gc.get_threshold()).

  • Thực hành: Dùng gc.get_threshold()/gc.get_count() kiểm tra ngưỡng và số lượng object hiện tại ở mỗi generation.

Ngày 849: Weak Reference

  • Mục tiêu: Học cách tránh cycle một cách chủ động.

  • Lý thuyết: weakref tạo tham chiếu không tăng refcount - hữu ích cho cache, observer pattern (liên hệ Giai đoạn 3) nơi object quan sát không nên "giữ sống" object bị quan sát.

  • Thực hành: Viết lại ví dụ Observer đã học Giai đoạn 3 (Ngày 164 - lapsed listener problem) bằng weakref, verify subject không giữ observer sống mãi.

Ngày 850: Lab - Phân tích Memory Leak bằng tracemalloc

  • Mục tiêu: Áp dụng toàn bộ kiến thức GC vào việc debug thực tế.

  • Lý thuyết: tracemalloc module built-in giúp trace nơi object được cấp phát, hữu ích tìm memory leak thực sự (thường do cycle không được GC kịp, hoặc do giữ reference không cần thiết).

  • Thực hành: Viết 1 chương trình cố tình leak memory (giữ reference không cần thiết trong 1 list global), dùng tracemalloc xác định chính xác dòng code gây leak.

Ngày 851: Review CPython Track

  • Mục tiêu: Tổng kết toàn bộ CPython Track (40 ngày).

  • Lý thuyết: Nhìn lại: PyObject → TypeObject → Bytecode → Descriptor → GIL → GC - đây chính là "hộp đen" mà mọi code Python/FastAPI/SQLAlchemy đã viết từ đầu roadmap chạy bên trong.

  • Thực hành: Viết báo cáo retrospective CPython Track, so sánh trực tiếp với JVM Track: điểm nào 2 runtime giống nhau về triết lý (cả 2 đều generational GC), điểm nào khác biệt căn bản (GIL vs true multi-threading, JIT vs pure interpreter).

PHẦN C: C++ ABI (Ngày 852–887)

Object Layout (Ngày 852–859)

Ngày 852: ABI - Giới thiệu

  • Mục tiêu: Hiểu khái niệm khác biệt với API.

  • Lý thuyết: Application Binary Interface - quy ước ở tầng binary (bố cục object trong memory, calling convention, name mangling) mà compiler phải tuân thủ để code compile riêng biệt vẫn link/chạy đúng với nhau.

  • Thực hành: Giải thích tại sao 2 thư viện C++ compile bằng 2 compiler khác nhau (VD: GCC và MSVC) thường không tương thích ABI dù cùng ngôn ngữ C++.

Ngày 853: Object Layout - Ôn sâu vtable/vptr

  • Mục tiêu: Đào sâu Giai đoạn 1 (Ngày 78-80) với chi tiết ABI cụ thể.

  • Lý thuyết: Ôn lại vtable/vptr, giờ xem xét vị trí chính xác của vptr trong object layout (thường ở đầu object) theo Itanium C++ ABI (chuẩn phổ biến nhất, dùng bởi GCC/Clang).

  • Thực hành: Dùng -fdump-class-hierarchy (GCC) hoặc công cụ tương đương, xem layout thật của 1 class có virtual function.

Ngày 854: Padding & Alignment

  • Mục tiêu: Hiểu vì sao sizeof() 1 struct không phải lúc nào cũng bằng tổng kích thước các field.

  • Lý thuyết: CPU đọc dữ liệu hiệu quả nhất khi dữ liệu align đúng địa chỉ bội số của kích thước nó (VD: int 4 byte nên ở địa chỉ chia hết cho 4) - compiler tự thêm padding (byte trống) giữa các field để đảm bảo alignment.

  • Thực hành: Viết 1 struct có field char, int, char xen kẽ, dùng sizeof() phát hiện padding, sắp xếp lại field theo kích thước giảm dần để giảm padding.

Ngày 855: Struct Layout - Cách Compiler Sắp Xếp Field

  • Mục tiêu: Hiểu quy tắc chung compiler dùng.

  • Lý thuyết: Field được sắp xếp theo đúng thứ tự khai báo (không tự động reorder để tối ưu, khác với 1 số ngôn ngữ khác), mỗi field align theo kích thước của chính nó, cả struct align theo field lớn nhất.

  • Thực hành: Thiết kế lại 1 struct có nhiều field kiểu khác nhau để tối thiểu hoá tổng kích thước (bằng cách sắp xếp lại thứ tự field), verify bằng sizeof().

Ngày 856: Empty Base Optimization

  • Mục tiêu: Học 1 tối ưu ABI thú vị cho inheritance.

  • Lý thuyết: Base class rỗng (không field) về lý thuyết vẫn cần sizeof() >= 1, nhưng khi làm base class của 1 class khác, compiler có thể áp dụng Empty Base Optimization để không tốn thêm byte nào cho phần base rỗng đó.

  • Thực hành: So sánh sizeof() của 1 class kế thừa từ base rỗng với class không kế thừa gì, verify EBO đang được áp dụng (kích thước bằng nhau).

Ngày 857: Name Mangling

  • Mục tiêu: Hiểu vì sao C++ symbol trong file object trông "kỳ lạ".

  • Lý thuyết: C++ hỗ trợ overload (nhiều hàm cùng tên khác tham số) nhưng linker chỉ làm việc với symbol name đơn - compiler "mangle" tên hàm (mã hoá thêm thông tin type tham số vào tên) để phân biệt.

  • Thực hành: Viết 2 hàm overload cùng tên khác tham số, compile, dùng nm xem mangled symbol name của cả 2, giải thích cách đọc.

Ngày 858: Lab - Dùng objdump/nm

  • Mục tiêu: Thực hành trực tiếp công cụ đọc binary.

  • Lý thuyết: Ôn lại nm (liệt kê symbol), objdump -d (disassemble, đã dùng ở Giai đoạn 0).

  • Thực hành: Compile 1 file C++ có class với virtual function, dùng c++filt demangle tên symbol về dạng đọc được, đối chiếu với source code.

Ngày 859: Review Object Layout

  • Mục tiêu: Củng cố nhóm.

  • Lý thuyết: Tổng hợp: vtable/vptr → Padding/Alignment → Struct Layout → EBO → Name Mangling.

  • Thực hành: Viết tổng kết: liên hệ ngược lại tại sao hiểu ABI quan trọng khi làm việc với thư viện C++ compiled sẵn (binary compatibility issue).

RTTI (Ngày 860–865)

Ngày 860: RTTI - Giới thiệu

  • Mục tiêu: Hiểu cơ chế lấy thông tin type lúc runtime trong C++.

  • Lý thuyết: Run-Time Type Information - C++ về cơ bản là static typing, nhưng RTTI cho phép truy vấn type thực sự của 1 object polymorphic lúc runtime (khi chỉ có con trỏ tới base class).

  • Thực hành: Giải thích vì sao RTTI chỉ hoạt động với class có ít nhất 1 virtual function (polymorphic class) - liên hệ lại vtable đã học.

Ngày 861: dynamic_cast

  • Mục tiêu: Hiểu cách RTTI được dùng phổ biến nhất.

  • Lý thuyết: dynamic_cast dùng RTTI để kiểm tra an toàn khi downcast (từ base xuống derived) - trả về nullptr (cho con trỏ) hoặc throw exception (cho reference) nếu cast không hợp lệ.

  • Thực hành: Viết ví dụ downcast an toàn bằng dynamic_cast, so sánh với static_cast không an toàn (compile được nhưng có thể gây undefined behavior nếu cast sai).

Ngày 862: typeid

  • Mục tiêu: Học cách lấy thông tin type trực tiếp.

  • Lý thuyết: typeid(obj) trả về type_info object mô tả type thực sự của object - dùng để so sánh type hoặc lấy tên type (dù tên có thể bị mangled tuỳ compiler).

  • Thực hành: Viết ví dụ dùng typeid so sánh type của 2 object polymorphic, in ra typeid(obj).name().

Ngày 863: Chi phí RTTI

  • Mục tiêu: Hiểu tại sao nhiều codebase production tắt RTTI (-fno-rtti).

  • Lý thuyết: RTTI thêm overhead (dữ liệu type_info cho mỗi polymorphic class, chi phí runtime cho dynamic_cast) - nhiều dự án embedded/game engine tắt RTTI để tối ưu, thay bằng cơ chế type tag tự viết.

  • Thực hành: Thiết kế 1 cơ chế "type tag" thủ công đơn giản (enum + field trong struct) làm thay thế dynamic_cast khi không có RTTI.

Ngày 864: Lab - Implement RTTI thủ công

  • Mục tiêu: Thực hành hiểu sâu bằng cách tự làm lại.

  • Lý thuyết: Ôn lại ý tưởng type tag Ngày 863.

  • Thực hành: Viết 1 hierarchy class nhỏ với type tag thủ công, viết hàm safeCast<T>() tự chế mô phỏng hành vi dynamic_cast dựa trên type tag.

Ngày 865: Review RTTI

  • Mục tiêu: Chốt lại nhóm.

  • Lý thuyết: Tổng hợp: RTTI là gì, dynamic_cast/typeid, chi phí, thay thế thủ công.

  • Thực hành: Viết nhận xét: khi nào dùng RTTI hợp lý (ứng dụng thông thường), khi nào nên cân nhắc tắt (performance-critical, embedded).

Templates (Ngày 866–873)

Ngày 866: Template - Giới thiệu

  • Mục tiêu: Hiểu generic programming trong C++ khác gì generic ở Java (đã học Giai đoạn 1 nếu có đề cập).

  • Lý thuyết: Template là cơ chế compile-time - mỗi lần dùng template với type khác nhau, compiler sinh ra 1 phiên bản code riêng biệt (khác Java generics dùng type erasure, chỉ có 1 phiên bản bytecode).

  • Thực hành: Viết 1 template function đơn giản (VD: max<T>), dùng với intdouble, giải thích compiler sinh ra 2 hàm riêng biệt.

Ngày 867: Template Instantiation

  • Mục tiêu: Hiểu chính xác quá trình compiler sinh code.

  • Lý thuyết: Template chỉ thực sự "biên dịch" khi được instantiate (dùng với type cụ thể) - đây là lý do lỗi template thường xuất hiện ở nơi sử dụng, không phải nơi định nghĩa.

  • Thực hành: Viết 1 template có lỗi chỉ xuất hiện với 1 số type cụ thể, verify code compile bình thường với type không gây lỗi, chỉ lỗi khi instantiate với type có vấn đề.

Ngày 868: Template Metaprogramming cơ bản

  • Mục tiêu: Học cách dùng template để tính toán ở compile-time.

  • Lý thuyết: Template có thể dùng để thực hiện tính toán ngay lúc compile (VD: tính giai thừa bằng template recursion) - nền tảng cho nhiều tối ưu compile-time trong thư viện C++ hiệu năng cao.

  • Thực hành: Viết 1 ví dụ kinh điển: tính giai thừa bằng template recursion (template<int N> struct Factorial), verify giá trị được tính lúc compile (không tốn chi phí runtime).

Ngày 869: SFINAE

  • Mục tiêu: Học kỹ thuật nâng cao để "chọn" template overload dựa trên điều kiện type.

  • Lý thuyết: Substitution Failure Is Not An Error - khi compiler thử instantiate 1 template mà substitution thất bại, nó không báo lỗi ngay mà loại overload đó ra khỏi candidate list, thử overload khác.

  • Thực hành: Đọc ví dụ SFINAE đơn giản dùng std::enable_if để chọn implementation khác nhau cho type là số nguyên vs không phải số nguyên.

Ngày 870: Concepts (C++20)

  • Mục tiêu: Học cách hiện đại thay thế SFINAE.

  • Lý thuyết: Concepts cho phép ràng buộc template parameter bằng cú pháp rõ ràng, dễ đọc hơn nhiều so với SFINAE, và cho thông báo lỗi compiler dễ hiểu hơn hẳn.

  • Thực hành: Viết lại ví dụ SFINAE Ngày 869 bằng Concepts (requires clause), so sánh độ dễ đọc.

Ngày 871: Template & Code Bloat

  • Mục tiêu: Hiểu chi phí thực tế của template.

  • Lý thuyết: Mỗi instantiation sinh ra 1 bản copy code riêng - dùng template với nhiều type khác nhau có thể làm binary phình to đáng kể (code bloat), ảnh hưởng cache instruction (liên hệ Giai đoạn 0).

  • Thực hành: Compile 1 template dùng với 10 type khác nhau, so sánh kích thước binary với 1 hàm non-template tương đương, quan sát chênh lệch.

Ngày 872: Lab - Template class với Validation

  • Mục tiêu: Thực hành kết hợp template với constraint.

  • Lý thuyết: Ôn lại static_assert/Concepts.

  • Thực hành: Viết 1 template class FixedBuffer<T, size_t N> chỉ chấp nhận type có thể copy được (dùng static_assert hoặc Concepts để validate compile-time).

Ngày 873: Review Templates

  • Mục tiêu: Chốt lại nhóm.

  • Lý thuyết: Tổng hợp: Instantiation → Metaprogramming → SFINAE → Concepts → Code Bloat.

  • Thực hành: Viết tổng kết liên hệ lại STL (std::vector<T> đã dùng suốt roadmap) - giờ hiểu rõ mỗi std::vector<int>std::vector<std::string> thực chất là 2 class hoàn toàn riêng biệt lúc compile.

Allocators (Ngày 874–881)

Ngày 874: Memory Allocator - Giới thiệu

  • Mục tiêu: Hiểu malloc/free hoạt động thế nào bên dưới.

  • Lý thuyết: Allocator quản lý 1 vùng memory lớn xin từ OS (qua brk/mmap), chia nhỏ và cấp phát cho chương trình theo yêu cầu - liên hệ lại Virtual Memory đã học Giai đoạn 0.

  • Thực hành: Vẽ sơ đồ minh hoạ: chương trình gọi malloc(100) → allocator tìm vùng trống phù hợp trong heap đã xin từ OS → trả về con trỏ.

Ngày 875: Allocator Internals - Free List

  • Mục tiêu: Hiểu cấu trúc dữ liệu allocator dùng để theo dõi vùng trống.

  • Lý thuyết: Free list - danh sách liên kết các block memory còn trống, mỗi block có header lưu kích thước - khi malloc được gọi, allocator duyệt free list tìm block đủ lớn (first-fit/best-fit).

  • Thực hành: Vẽ sơ đồ free list với vài block trống kích thước khác nhau, mô phỏng thủ công malloc(50) tìm block phù hợp bằng first-fit.

Ngày 876: Custom Allocator trong C++

  • Mục tiêu: Hiểu interface chuẩn C++ dùng để tuỳ biến allocation.

  • Lý thuyết: std::allocator interface (allocate()/deallocate()) - mọi container STL (vector, map...) đều nhận allocator tuỳ chỉnh làm template parameter thứ 2, mặc định dùng std::allocator (bọc malloc/free).

  • Thực hành: Đọc interface std::allocator, xác định các method bắt buộc cần implement để tạo custom allocator hợp lệ.

Ngày 877: Memory Pool Allocator

  • Mục tiêu: Học loại allocator tối ưu cho pattern allocation cụ thể.

  • Lý thuyết: Pool Allocator cấp phát trước 1 vùng lớn, chia thành các block cùng kích thước cố định - cấp phát/giải phóng cực nhanh (O(1), chỉ cần push/pop free list) nhưng chỉ phù hợp khi biết trước kích thước object.

  • Thực hành: Vẽ sơ đồ Pool Allocator cho object kích thước cố định (VD: Node của linked list), so sánh tốc độ lý thuyết với malloc/free chung.

Ngày 878: Arena Allocator

  • Mục tiêu: Học loại allocator khác, tối ưu cho pattern "cấp phát nhiều, giải phóng cùng lúc".

  • Lý thuyết: Arena (Bump Allocator) chỉ tăng con trỏ mỗi lần cấp phát (cực nhanh), không hỗ trợ giải phóng từng object riêng lẻ - giải phóng toàn bộ arena cùng lúc khi không cần nữa (phù hợp: xử lý 1 request rồi dọn sạch toàn bộ).

  • Thực hành: Vẽ sơ đồ Arena Allocator, giải thích vì sao nó không hỗ trợ free() cho từng object, chỉ có reset() toàn bộ arena.

Ngày 879: jemalloc/tcmalloc

  • Mục tiêu: Biết allocator production-grade thay thế malloc mặc định.

  • Lý thuyết: jemalloc (Facebook)/tcmalloc (Google) tối ưu cho đa luồng (mỗi thread có arena riêng giảm lock contention - liên hệ Giai đoạn 5), giảm fragmentation tốt hơn allocator mặc định của glibc.

  • Thực hành: Đọc benchmark công khai so sánh jemalloc/tcmalloc với malloc mặc định cho ứng dụng đa luồng, tóm tắt kết quả chính.

Ngày 880: Lab - Viết Memory Pool Allocator

  • Mục tiêu: Thực hành hoá lý thuyết Ngày 877.

  • Lý thuyết: Ôn lại free list cho fixed-size block.

  • Thực hành: Cài đặt PoolAllocator<T> với allocate()/deallocate() O(1), test với 1 struct kích thước cố định, benchmark so với new/delete thông thường.

Ngày 881: Review Allocators

  • Mục tiêu: Chốt lại nhóm.

  • Lý thuyết: Tổng hợp: malloc/free cơ bản → Custom Allocator interface → Pool → Arena → jemalloc/tcmalloc.

  • Thực hành: Lập bảng quyết định: với 3 tình huống khác nhau (cấp phát object cùng kích thước liên tục, xử lý 1 request rồi dọn sạch, ứng dụng đa luồng chung), chọn loại allocator phù hợp.

Smart Pointer Internals (Ngày 882–887)

Ngày 882: unique_ptr Internals

  • Mục tiêu: Hiểu "zero-overhead abstraction" nghĩa là gì cụ thể.

  • Lý thuyết: unique_ptr về cơ bản chỉ là 1 con trỏ thô được wrap, không có overhead runtime (không có reference count, không có control block) - destructor tự động gọi delete, đây chính là RAII thuần tuý (Giai đoạn 1).

  • Thực hành: So sánh sizeof(unique_ptr<T>) với sizeof(T*) thô, verify chúng bằng nhau (zero overhead).

Ngày 883: shared_ptr Internals - Control Block

  • Mục tiêu: Hiểu chi phí thực sự của shared_ptr, khác hẳn unique_ptr.

  • Lý thuyết: shared_ptr cần thêm 1 "control block" (cấp phát riêng trên heap) chứa: strong reference count, weak reference count, deleter - đây là lý do sizeof(shared_ptr<T>) lớn hơn 1 con trỏ thô (thường gấp đôi, vì có thêm con trỏ tới control block).

  • Thực hành: So sánh sizeof(shared_ptr<T>) với sizeof(T*), giải thích phần chênh lệch chính là con trỏ tới control block.

Ngày 884: weak_ptr Internals

  • Mục tiêu: Liên hệ lại weakref của Python (Ngày 849) - cùng ý tưởng, khác implementation.

  • Lý thuyết: weak_ptr tham chiếu tới cùng control block nhưng chỉ tăng weak reference count (không tăng strong count) - object thực sự bị delete khi strong count về 0, dù weak count còn > 0 (control block vẫn tồn tại để weak_ptr biết object đã chết).

  • Thực hành: Viết ví dụ minh hoạ: 2 object tham chiếu vòng tròn bằng shared_ptr (gây leak), sửa 1 chiều thành weak_ptr để phá vỡ cycle - tương tự bài toán Observer đã giải bằng weakref ở Python (Ngày 849).

Ngày 885: Custom Deleter

  • Mục tiêu: Học cách tuỳ biến hành vi giải phóng resource.

  • Lý thuyết: shared_ptr/unique_ptr có thể nhận custom deleter (function/lambda) thay vì delete mặc định - hữu ích cho resource không phải cấp phát bằng new (VD: file handle, socket).

  • Thực hành: Viết unique_ptr với custom deleter tự động đóng file handle thay vì gọi delete.

Ngày 886: Lab - Tự viết shared_ptr đơn giản

  • Mục tiêu: Chuẩn bị trực tiếp cho Project cuối giai đoạn.

  • Lý thuyết: Ôn lại toàn bộ control block, strong/weak count.

  • Thực hành: Bắt đầu cài đặt MySharedPtr<T> với control block riêng, hỗ trợ copy constructor tăng strong count, destructor giảm strong count và delete khi về 0.

Ngày 887: Review Smart Pointer Internals - Chốt C++ ABI Track

  • Mục tiêu: Tổng kết toàn bộ C++ ABI Track (36 ngày).

  • Lý thuyết: Nhìn lại: Object Layout → RTTI → Templates → Allocators → Smart Pointer - đây chính là những gì STL và mọi thư viện C++ hiệu năng cao (RocksDB, Envoy đã học Giai đoạn 10) xây dựng bên trên.

  • Thực hành: Viết báo cáo retrospective C++ ABI Track, so sánh triết lý "trả tiền cho thứ bạn dùng" (zero-overhead abstraction) của C++ với JVM/CPython (luôn có overhead runtime managed).

Nhóm cuối: Project - Tự viết shared_ptr, vector, mini allocator (Ngày 888–903)

Ngày 888: Thiết kế Project

  • Mục tiêu: Lên kế hoạch tích hợp 3 thành phần đã học rời rạc.

  • Lý thuyết: Ôn lại: MySharedPtr (Ngày 886), vector (đã viết sơ khai ở Giai đoạn 0, giờ nâng cấp với allocator-aware design), PoolAllocator (Ngày 880) - mục tiêu cuối là 1 vector dùng MyPoolAllocator tự viết, chứa MySharedPtr object.

  • Thực hành: Vẽ sơ đồ kiến trúc 3 thành phần và cách chúng tương tác trong project cuối.

Ngày 889: Implement shared_ptr - Control Block

  • Mục tiêu: Hoàn thiện phần đã bắt đầu Ngày 886.

  • Lý thuyết: Ôn lại cấu trúc control block (strong count, weak count, con trỏ object, deleter).

  • Thực hành: Hoàn thiện MySharedPtr<T> với control block đầy đủ, constructor cấp phát control block cùng lúc với object (tối ưu giống std::make_shared).

Ngày 890: Implement shared_ptr - Copy/Move Semantics

  • Mục tiêu: Đảm bảo hành vi đúng với Rule of Five (Giai đoạn 1, Ngày 51).

  • Lý thuyết: Copy constructor tăng strong count, move constructor "cướp" control block mà không tăng count (tối ưu hơn copy), destructor giảm count và giải phóng nếu về 0.

  • Thực hành: Implement đầy đủ copy constructor, copy assignment, move constructor, move assignment cho MySharedPtr.

Ngày 891: Implement shared_ptr - Custom Deleter

  • Mục tiêu: Hỗ trợ tính năng đã học Ngày 885.

  • Lý thuyết: Ôn lại cách lưu deleter (thường qua type erasure - std::function hoặc kỹ thuật tương đương) trong control block.

  • Thực hành: Thêm khả năng nhận custom deleter vào constructor MySharedPtr.

Ngày 892: Implement weak_ptr

  • Mục tiêu: Hoàn thiện bộ 3 con trỏ thông minh.

  • Lý thuyết: Ôn lại Ngày 884 - weak_ptr chia sẻ control block nhưng chỉ tăng weak count.

  • Thực hành: Implement MyWeakPtr<T>, hỗ trợ lock() trả về MySharedPtr nếu object còn sống, nullptr/rỗng nếu đã chết.

Ngày 893: Test shared_ptr

  • Mục tiêu: Verify tính đúng đắn so với std::shared_ptr.

  • Lý thuyết: Ôn lại Testing Strategy (Giai đoạn 4).

  • Thực hành: Viết test cho MySharedPtr/MyWeakPtr: reference counting đúng, không leak, không double-free, verify cycle vẫn leak khi dùng toàn shared_ptr (như lý thuyết) và không leak khi có weak_ptr phá cycle.

Ngày 894: Implement vector - Allocator-aware Design

  • Mục tiêu: Nâng cấp vector sơ khai từ Giai đoạn 0 lên chuẩn thiết kế thật của STL.

  • Lý thuyết: Ôn lại std::allocator interface (Ngày 876) - vector nên nhận allocator làm template parameter, dùng nó cho mọi lần cấp phát thay vì gọi new/delete trực tiếp.

  • Thực hành: Sửa MyVector<T, Allocator = std::allocator<T>>, dùng Allocator::allocate()/deallocate() thay vì new/delete.

Ngày 895: Implement vector - Growth Strategy & Exception Safety

  • Mục tiêu: Hoàn thiện hành vi đúng chuẩn.

  • Lý thuyết: Ôn lại growth factor (Giai đoạn 0, Ngày 9) - thêm exception safety guarantee (nếu constructor của T throw giữa lúc resize, vector không được để lại trạng thái hỏng).

  • Thực hành: Implement push_back/resize với strong exception safety (dùng kỹ thuật copy-and-swap hoặc tương đương).

Ngày 896: Implement vector - Iterator Support

  • Mục tiêu: Hỗ trợ range-based for loop và thuật toán STL.

  • Lý thuyết: Iterator cần implement đủ operator (++, *, !=) để tương thích với STL algorithm và range-based for.

  • Thực hành: Implement iterator/const_iterator cho MyVector, verify hoạt động với std::sort() và range-based for loop.

Ngày 897: Test vector

  • Mục tiêu: Verify tính đúng đắn so với std::vector.

  • Lý thuyết: Ôn lại test đã viết ở Giai đoạn 0 (Ngày 35), giờ mở rộng thêm cho allocator-aware design.

  • Thực hành: Viết test đầy đủ cho MyVector: push_back, resize, iterator, exception safety khi constructor throw.

Ngày 898: Implement Mini Allocator - Free List

  • Mục tiêu: Hoàn thiện allocator tổng quát (không chỉ fixed-size như Pool Ngày 880).

  • Lý thuyết: Ôn lại free list (Ngày 875) - cần xử lý allocation kích thước thay đổi, không chỉ 1 kích thước cố định.

  • Thực hành: Cài đặt MiniAllocator với free list hỗ trợ nhiều kích thước khác nhau, dùng first-fit strategy.

Ngày 899: Implement Mini Allocator - Pool cho Fixed-size Object

  • Mục tiêu: Kết hợp cả 2 chiến lược đã học.

  • Lý thuyết: Ôn lại Pool Allocator (Ngày 877, 880) hiệu quả hơn free list tổng quát khi biết trước kích thước.

  • Thực hành: Thêm chế độ "pool mode" vào MiniAllocator khi biết trước object luôn cùng kích thước (dùng bởi MySharedPtr control block, luôn cùng kích thước).

Ngày 900: Tích hợp Mini Allocator vào Vector

  • Mục tiêu: Ghép 3 thành phần lại thành 1 hệ thống hoàn chỉnh.

  • Lý thuyết: Ôn lại MyVector<T, Allocator> đã thiết kế allocator-aware ở Ngày 894.

  • Thực hành: Dùng MyVector<MySharedPtr<SomeType>, MiniAllocator<...>> - verify toàn bộ 3 thành phần tự viết hoạt động cùng nhau.

Ngày 901: Benchmark toàn bộ 3 thành phần vs STL

  • Mục tiêu: Đánh giá khách quan công sức 140 ngày vừa bỏ ra.

  • Lý thuyết: Ôn lại benchmark methodology đã dùng xuyên suốt roadmap.

  • Thực hành: Benchmark MySharedPtr vs std::shared_ptr, MyVector vs std::vector, MiniAllocator vs std::allocator, phân tích chênh lệch hiệu năng và nguyên nhân (thiếu tối ưu nào so với implementation thật của STL).

Ngày 902: Test Tổng hợp

  • Mục tiêu: Đảm bảo toàn bộ hệ thống tích hợp hoạt động đúng.

  • Lý thuyết: Ôn lại Testing Strategy cho toàn bộ integration.

  • Thực hành: Viết test end-to-end: tạo nhiều MySharedPtr object trong MyVector dùng MiniAllocator, verify không leak, không crash qua nhiều thao tác insert/remove.

Ngày 903: Retrospective Giai đoạn 14

  • Mục tiêu: Tổng kết toàn bộ giai đoạn dài thứ 2 trong roadmap (140 ngày, 3 track + project).

  • Lý thuyết: Nhìn lại hành trình: JVM Internals → CPython Internals → C++ ABI → Project tổng hợp - đây là tầng sâu nhất của roadmap, nơi mọi framework/pattern/kiến trúc đã học từ đầu thực sự "chạm đáy".

  • Thực hành: Viết báo cáo tổng kết lớn: so sánh triết lý 3 runtime (managed+JIT của JVM, managed+interpreter+GIL của CPython, unmanaged+zero-overhead của C++), và tự đánh giá bạn hiểu framework/thư viện hàng ngày dùng sâu hơn bao nhiêu so với trước khi bắt đầu Giai đoạn 14.


Knowledge

Part 1 of 50