1package memsize 2 3import ( 4 "testing" 5 "unsafe" 6) 7 8const ( 9 sizeofSlice = unsafe.Sizeof([]byte{}) 10 sizeofMap = unsafe.Sizeof(map[string]string{}) 11 sizeofInterface = unsafe.Sizeof((interface{})(nil)) 12 sizeofString = unsafe.Sizeof("") 13 sizeofWord = unsafe.Sizeof(uintptr(0)) 14 sizeofChan = unsafe.Sizeof(make(chan struct{})) 15) 16 17type ( 18 struct16 struct { 19 x, y uint64 20 } 21 structptr struct { 22 x uint32 23 cld *structptr 24 } 25 structuint32ptr struct { 26 x *uint32 27 } 28 structmultiptr struct { 29 s1 *structptr 30 u1 *structuint32ptr 31 s2 *structptr 32 u2 *structuint32ptr 33 s3 *structptr 34 u3 *structuint32ptr 35 } 36 structarrayptr struct { 37 x *uint64 38 a [10]uint64 39 } 40 structiface struct { 41 s *struct16 42 x interface{} 43 } 44 struct64array struct{ array64 } 45 structslice struct{ s []uint32 } 46 structstring struct{ s string } 47 structloop struct{ s *structloop } 48 structptrslice struct{ s *structslice } 49 array64 [64]byte 50) 51 52func TestTotal(t *testing.T) { 53 tests := []struct { 54 name string 55 v interface{} 56 want uintptr 57 }{ 58 { 59 name: "struct16", 60 v: &struct16{}, 61 want: 16, 62 }, 63 { 64 name: "structptr_nil", 65 v: &structptr{}, 66 want: 2 * sizeofWord, 67 }, 68 { 69 name: "structptr", 70 v: &structptr{cld: &structptr{}}, 71 want: 2 * 2 * sizeofWord, 72 }, 73 { 74 name: "structptr_loop", 75 v: func() *structptr { 76 v := &structptr{} 77 v.cld = v 78 return v 79 }(), 80 want: 2 * sizeofWord, 81 }, 82 { 83 name: "structmultiptr_loop", 84 v: func() *structmultiptr { 85 v1 := &structptr{x: 1} 86 v2 := &structptr{x: 2, cld: v1} 87 return &structmultiptr{s1: v1, s2: v1, s3: v2} 88 }(), 89 want: 6*sizeofWord /* structmultiptr */ + 2*2*sizeofWord, /* structptr */ 90 }, 91 { 92 name: "structmultiptr_interior", 93 v: func() *structmultiptr { 94 v1 := &structptr{x: 1} 95 v2 := &structptr{x: 2} 96 return &structmultiptr{ 97 // s1 is scanned before u1, which has a reference to a field of s1. 98 s1: v1, 99 u1: &structuint32ptr{x: &v1.x}, 100 // This one goes the other way around: u2, which has a reference to a 101 // field of s3 is scanned before s3. 102 u2: &structuint32ptr{x: &v2.x}, 103 s3: v2, 104 } 105 }(), 106 want: 6*sizeofWord /* structmultiptr */ + 2*2*sizeofWord /* structptr */ + 2*sizeofWord, /* structuint32ptr */ 107 }, 108 { 109 name: "struct64array", 110 v: &struct64array{}, 111 want: 64, 112 }, 113 { 114 name: "structptrslice", 115 v: &structptrslice{&structslice{s: []uint32{1, 2, 3}}}, 116 want: sizeofWord + sizeofSlice + 3*4, 117 }, 118 { 119 name: "array_unadressable", 120 v: func() *map[[3]uint64]struct{} { 121 v := map[[3]uint64]struct{}{ 122 {1, 2, 3}: struct{}{}, 123 } 124 return &v 125 }(), 126 want: sizeofMap + 3*8, 127 }, 128 { 129 name: "structslice", 130 v: &structslice{s: []uint32{1, 2, 3}}, 131 want: sizeofSlice + 3*4, 132 }, 133 { 134 name: "structloop", 135 v: func() *structloop { 136 v := new(structloop) 137 v.s = v 138 return v 139 }(), 140 want: sizeofWord, 141 }, 142 { 143 name: "array64", 144 v: &array64{}, 145 want: 64, 146 }, 147 { 148 name: "byteslice", 149 v: &[]byte{1, 2, 3}, 150 want: sizeofSlice + 3, 151 }, 152 { 153 name: "slice3_ptrval", 154 v: &[]*struct16{{}, {}, {}}, 155 want: sizeofSlice + 3*sizeofWord + 3*16, 156 }, 157 { 158 name: "map3", 159 v: &map[uint64]uint64{1: 1, 2: 2, 3: 3}, 160 want: sizeofMap + 3*8 /* keys */ + 3*8, /* values */ 161 }, 162 { 163 name: "map3_ptrval", 164 v: &map[uint64]*struct16{1: {}, 2: {}, 3: {}}, 165 want: sizeofMap + 3*8 /* keys */ + 3*sizeofWord /* value pointers */ + 3*16, /* values */ 166 }, 167 { 168 name: "map3_ptrkey", 169 v: &map[*struct16]uint64{{x: 1}: 1, {x: 2}: 2, {x: 3}: 3}, 170 want: sizeofMap + 3*sizeofWord /* key pointers */ + 3*16 /* keys */ + 3*8, /* values */ 171 }, 172 { 173 name: "map_interface", 174 v: &map[interface{}]interface{}{"aa": uint64(1)}, 175 want: sizeofMap + sizeofInterface + sizeofString + 2 /* key */ + sizeofInterface + 8, /* value */ 176 }, 177 { 178 name: "pointerpointer", 179 v: func() **uint64 { 180 i := uint64(0) 181 p := &i 182 return &p 183 }(), 184 want: sizeofWord + 8, 185 }, 186 { 187 name: "structstring", 188 v: &structstring{"123"}, 189 want: sizeofString + 3, 190 }, 191 { 192 name: "slices_samearray", 193 v: func() *[3][]byte { 194 backarray := [64]byte{} 195 return &[3][]byte{ 196 backarray[16:], 197 backarray[4:16], 198 backarray[0:4], 199 } 200 }(), 201 want: 3*sizeofSlice + 64, 202 }, 203 { 204 name: "slices_nil", 205 v: func() *[2][]byte { 206 return &[2][]byte{nil, nil} 207 }(), 208 want: 2 * sizeofSlice, 209 }, 210 { 211 name: "slices_overlap_total", 212 v: func() *[2][]byte { 213 backarray := [32]byte{} 214 return &[2][]byte{backarray[:], backarray[:]} 215 }(), 216 want: 2*sizeofSlice + 32, 217 }, 218 { 219 name: "slices_overlap", 220 v: func() *[4][]uint16 { 221 backarray := [32]uint16{} 222 return &[4][]uint16{ 223 backarray[2:4], 224 backarray[10:12], 225 backarray[20:25], 226 backarray[:], 227 } 228 }(), 229 want: 4*sizeofSlice + 32*2, 230 }, 231 { 232 name: "slices_overlap_array", 233 v: func() *struct { 234 a [32]byte 235 s [2][]byte 236 } { 237 v := struct { 238 a [32]byte 239 s [2][]byte 240 }{} 241 v.s[0] = v.a[2:4] 242 v.s[1] = v.a[5:8] 243 return &v 244 }(), 245 want: 32 + 2*sizeofSlice, 246 }, 247 { 248 name: "interface", 249 v: &[2]interface{}{uint64(0), &struct16{}}, 250 want: 2*sizeofInterface + 8 + 16, 251 }, 252 { 253 name: "interface_nil", 254 v: &[2]interface{}{nil, nil}, 255 want: 2 * sizeofInterface, 256 }, 257 { 258 name: "structiface_slice", 259 v: &structiface{x: make([]byte, 10)}, 260 want: sizeofWord + sizeofInterface + sizeofSlice + 10, 261 }, 262 { 263 name: "structiface_pointer", 264 v: func() *structiface { 265 s := &struct16{1, 2} 266 return &structiface{s: s, x: &s.x} 267 }(), 268 want: sizeofWord + 16 + sizeofInterface, 269 }, 270 { 271 name: "empty_chan", 272 v: func() *chan uint64 { 273 c := make(chan uint64) 274 return &c 275 }(), 276 want: sizeofChan, 277 }, 278 { 279 name: "empty_closed_chan", 280 v: func() *chan uint64 { 281 c := make(chan uint64) 282 close(c) 283 return &c 284 }(), 285 want: sizeofChan, 286 }, 287 { 288 name: "empty_chan_buffer", 289 v: func() *chan uint64 { 290 c := make(chan uint64, 10) 291 return &c 292 }(), 293 want: sizeofChan + 10*8, 294 }, 295 { 296 name: "chan_buffer", 297 v: func() *chan uint64 { 298 c := make(chan uint64, 10) 299 for i := 0; i < 8; i++ { 300 c <- 0 301 } 302 return &c 303 }(), 304 want: sizeofChan + 10*8, 305 }, 306 { 307 name: "closed_chan_buffer", 308 v: func() *chan uint64 { 309 c := make(chan uint64, 10) 310 for i := 0; i < 8; i++ { 311 c <- 0 312 } 313 close(c) 314 return &c 315 }(), 316 want: sizeofChan + 10*8, 317 }, 318 { 319 name: "chan_buffer_escan", 320 v: func() *chan *struct16 { 321 c := make(chan *struct16, 10) 322 for i := 0; i < 8; i++ { 323 c <- &struct16{x: uint64(i)} 324 } 325 return &c 326 }(), 327 want: sizeofChan + 10*sizeofWord + 8*16, 328 }, 329 { 330 name: "closed_chan_buffer_escan", 331 v: func() *chan *struct16 { 332 c := make(chan *struct16, 10) 333 for i := 0; i < 8; i++ { 334 c <- &struct16{x: uint64(i)} 335 } 336 close(c) 337 return &c 338 }(), 339 want: sizeofChan + 10*sizeofWord + 8*16, 340 }, 341 { 342 name: "nil_chan", 343 v: func() *chan *struct16 { 344 var c chan *struct16 345 return &c 346 }(), 347 want: sizeofChan, 348 }, 349 } 350 for _, test := range tests { 351 t.Run(test.name, func(t *testing.T) { 352 size := Scan(test.v) 353 if size.Total != test.want { 354 t.Errorf("total=%d, want %d", size.Total, test.want) 355 t.Logf("\n%s", size.Report()) 356 } 357 }) 358 } 359} 360