Syllabus software engineering (7)
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
ClassLoadercủaString.classvà 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.Stringtuỳ 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:classrằ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ànhClassobject.Thực hành: Viết
CustomClassLoaderload class từ 1 file.classnằ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
.classtồ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êminvokestatic(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 -cxá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 -vxem 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/whilecompile 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
forloop đơn giản, dùngjavap -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 -vphâ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:+PrintCompilationquan 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ậnDouble→ 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/synchronizedkhô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
volatilelàm gì ở tầng dưới, không chỉ "đảm bảo visibility".Lý thuyết:
volatilechè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
volatileso 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
finalfield trong JMM.Lý thuyết: JMM đảm bảo nếu constructor không "leak"
thisra ngoài (object chưa hoàn thành khởi tạo), thì mọi thread khác thấyfinalfield đã đượ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ó
finalfield 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:
PyObjectlà struct C gốc mọi object Python kế thừa - chứaob_refcnt(reference count) vàob_type(con trỏ tới type object).Thực hành: Vẽ sơ đồ struct
PyObjectcơ 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:
PyListObjectchứ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 1PyObjectriê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
isoperator kiểm traa = 100; b = 100; a is b(True, do cache) vsa = 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
isoperator 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
ctypescho phép truy cập raw memory từ Python.Thực hành: Dùng
ctypesđọcob_refcnttrực tiếp từ 1 object Python, verify khớp vớisys.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:
PyTypeObjectmô tả 1 type (VD:int,list, hay class tự định nghĩa) - bản thân nó cũng là 1PyObject(type củaintlàtype).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:
PyTypeObjectlưu sẵntp_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ùngsys.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 attributequacktrong__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ớijavap -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ặcCALLở 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
Frameobject 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.cchứ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 (
javapvsdis).
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_FASTnhanh hơnLOAD_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 choattrkhô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:
propertylà 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ứ@propertylàm được, tự viết descriptor cũng làm được.Thực hành: Viết lại 1 ví dụ dùng
@propertybằ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
TrackedFieldtự độ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
@propertyvà 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
PyObjecttự 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_THREADSvừ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:
weakreftạ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:
tracemallocmodule 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
tracemallocxá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:
int4 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,charxen kẽ, dùngsizeof()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
nmxem 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++filtdemangle 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_castdù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ớistatic_castkhô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_infoobject 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
typeidso sánh type của 2 object polymorphic, in ratypeid(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_castkhi 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 vidynamic_castdự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ớiintvàdouble, 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 (
requiresclause), 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ùngstatic_asserthoặ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ỗistd::vector<int>và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/freehoạ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::allocatorinterface (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ùngstd::allocator(bọcmalloc/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:
Nodecủa linked list), so sánh tốc độ lý thuyết vớimalloc/freechung.
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ế
mallocmặ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ớiallocate()/deallocate()O(1), test với 1 struct kích thước cố định, benchmark so vớinew/deletethô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_ptrvề 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ọidelete, đây chính là RAII thuần tuý (Giai đoạn 1).Thực hành: So sánh
sizeof(unique_ptr<T>)vớisizeof(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ẳnunique_ptr.Lý thuyết:
shared_ptrcầ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ý dosizeof(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ớisizeof(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_ptrtham 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ịdeletekhi strong count về 0, dù weak count còn > 0 (control block vẫn tồn tại đểweak_ptrbiế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ànhweak_ptrđể phá vỡ cycle - tương tự bài toán Observer đã giải bằngweakrefở 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_ptrcó thể nhận custom deleter (function/lambda) thay vìdeletemặc định - hữu ích cho resource không phải cấp phát bằngnew(VD: file handle, socket).Thực hành: Viết
unique_ptrvới custom deleter tự động đóng file handle thay vì gọidelete.
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àdeletekhi 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à 1vectordùngMyPoolAllocatortự viết, chứaMySharedPtrobject.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ốngstd::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::functionhoặ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_ptrchia sẻ control block nhưng chỉ tăng weak count.Thực hành: Implement
MyWeakPtr<T>, hỗ trợlock()trả vềMySharedPtrnế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ànshared_ptr(như lý thuyết) và không leak khi cóweak_ptrphá cycle.
Ngày 894: Implement vector - Allocator-aware Design
Mục tiêu: Nâng cấp
vectorsơ 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::allocatorinterface (Ngày 876) -vectornên nhận allocator làm template parameter, dùng nó cho mọi lần cấp phát thay vì gọinew/deletetrực tiếp.Thực hành: Sửa
MyVector<T, Allocator = std::allocator<T>>, dùngAllocator::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
Tthrow giữa lúc resize,vectorkhô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_iteratorchoMyVector, verify hoạt động vớistd::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
MiniAllocatorvớ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
MiniAllocatorkhi biết trước object luôn cùng kích thước (dùng bởiMySharedPtrcontrol 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
MySharedPtrvsstd::shared_ptr,MyVectorvsstd::vector,MiniAllocatorvsstd::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
MySharedPtrobject trongMyVectordùngMiniAllocator, 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.