Arrange so that adjacent rows are from the same protection domain. Perhaps a memory controller can arrange that a row of one (4K) page is never adjacent to that of a different page. This would waste some amount of memory.

My Macs:
(2015) 2 DIMMs: each 4 GB, DDR3, 1067 MHz, Manuf = 0x859B.
(2017) 2 DIMMs: each 8 GB, DDR3, 1600 MHz, Manuf = 0x80AD.

Assuming 8 chips/DIMM I get 4 Gb / chip.
Assuming 4 banks per chip I get 1Gb/bank.
√G = 32K.
A 4KB page (memory map unit) is 32Kb.
Oops, I get that a page is a chip row. Worse a page is normally distributed as ¼ of each of 4 rows on different chips.

On the subject of memory chips, I think that several registers per bank might be strategic. On chip cache. Several rows open per bank. Managed by memory controller. Possible controller strategy: leave recently accessed rows open but lazily close less used rows in order normally avoid closing a row in order to open a new row.

See this. pretty good description of SDRAM logic.

Another scheme is to implement 2n decaying counters, perhaps analog, for some n that you can afford. For each line refresh of line x originated by a suspect source, increment counter x mod 2n. If the counter gets too big, zero it and refresh both of the neighbor lines.

Forget the above. A bank on a DDR3 chip has n rows of n bits each. n is some power of 2, perhaps 212. For each bank there are k (1, 4, ?) regen registers known to the memory controller. From that bank k rows can be open at once. The controller is responsible for remembering which regen register goes where among the n rows. A read cycle consists of destructively sensing the charges in the row, and transferring those n bits to one of the regen registers selected by the controller. Lest the data be lost the controller must command a write cycle to restore the contents of the regen register to the proper row.

The row-hammer fix proposal is to add a few (8?) bit counter to each row and to each regen register. The counter is incremented on each write signal. When the counter wraps a signal is sent to the controller that presumably refreshes either neighbor. Whenever a row is refreshed its counter is reset. The counter measures potential damage to its two neighbors, malicious or otherwise.

Forget the above (for now). Most SDDRam chips today inhabit DIMMs, 8 chips per DIMM, and most computer systems have a memory controller with N (a few) DIMMs. Each chip has 2M rows. Each row (on a chip) has 2K bits. M and K are about 15. This memory system provides N2KM MB of memory. Here is a solution at the memory controller level. It assumes a slow volatile mutable memory, C, of N2M words each of J (<8) bits. A long row consists of the same row in each of the 8 chips of a DIMM. A long row has 8(2K) bits. For each element of C there are J bits or a ratio of one bit in C to guard 2K bits of DRAM.

When the controller opens long row j, it sets C[j] to 0 and increments both C[j–1] and C[j+1]. If either of these overflow it refreshes the corresponding long row. This additional logic should not be in the critical path. It fixes innocent and malicious cases.